diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-08-07 14:56:50 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-09-21 10:29:37 +0200 |
commit | c37f9fba70085fedc8eede7559489d2321393005 (patch) | |
tree | 042455ebf1fa89a277a825f72e1ed805d0b4d296 /freebsd/sys | |
parent | Update to FreeBSD head 2017-06-01 (diff) | |
download | rtems-libbsd-c37f9fba70085fedc8eede7559489d2321393005.tar.bz2 |
Update to FreeBSD head 2017-08-01
Git mirror commit f5002f5e5f78cae9f0269d812dc0aedb0339312c.
Update #3472.
Diffstat (limited to 'freebsd/sys')
137 files changed, 6075 insertions, 2794 deletions
diff --git a/freebsd/sys/arm/ti/ti_sdhci.c b/freebsd/sys/arm/ti/ti_sdhci.c index 94096fd6..76e4b750 100644 --- a/freebsd/sys/arm/ti/ti_sdhci.c +++ b/freebsd/sys/arm/ti/ti_sdhci.c @@ -41,6 +41,8 @@ __FBSDID("$FreeBSD$"); #include <sys/rman.h> #include <sys/sysctl.h> #include <sys/taskqueue.h> +#include <sys/lock.h> +#include <sys/mutex.h> #include <machine/bus.h> #include <machine/resource.h> @@ -62,6 +64,8 @@ __FBSDID("$FreeBSD$"); #include <arm/ti/ti_hwmods.h> #include <rtems/bsd/local/gpio_if.h> +#include <rtems/bsd/local/opt_mmccam.h> + struct ti_sdhci_softc { device_t dev; struct sdhci_fdt_gpio * gpio; @@ -124,6 +128,11 @@ static struct ofw_compat_data compat_data[] = { #define MMCHS_SD_CAPA_VS30 (1 << 25) #define MMCHS_SD_CAPA_VS33 (1 << 24) +/* Forward declarations, CAM-relataed */ +// static void ti_sdhci_cam_poll(struct cam_sim *); +// static void ti_sdhci_cam_action(struct cam_sim *, union ccb *); +// static int ti_sdhci_cam_settran_settings(struct ti_sdhci_softc *sc, union ccb *); + static inline uint32_t ti_mmchs_read_4(struct ti_sdhci_softc *sc, bus_size_t off) { @@ -243,6 +252,22 @@ ti_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, struct ti_sdhci_softc *sc = device_get_softc(dev); uint32_t val32; +#ifdef MMCCAM + uint32_t newval32; + if (off == SDHCI_HOST_CONTROL) { + val32 = ti_mmchs_read_4(sc, MMCHS_CON); + newval32 = val32; + if (val & SDHCI_CTRL_8BITBUS) { + device_printf(dev, "Custom-enabling 8-bit bus\n"); + newval32 |= MMCHS_CON_DW8; + } else { + device_printf(dev, "Custom-disabling 8-bit bus\n"); + newval32 &= ~MMCHS_CON_DW8; + } + if (newval32 != val32) + ti_mmchs_write_4(sc, MMCHS_CON, newval32); + } +#endif val32 = RD4(sc, off & ~3); val32 &= ~(0xff << (off & 3) * 8); val32 |= (val << (off & 3) * 8); @@ -660,8 +685,11 @@ ti_sdhci_attach(device_t dev) bus_generic_probe(dev); bus_generic_attach(dev); +#ifdef MMCCAM + sdhci_cam_start_slot(&sc->slot); +#else sdhci_start_slot(&sc->slot); - +#endif return (0); fail: @@ -732,4 +760,7 @@ static driver_t ti_sdhci_driver = { DRIVER_MODULE(sdhci_ti, simplebus, ti_sdhci_driver, ti_sdhci_devclass, NULL, NULL); MODULE_DEPEND(sdhci_ti, sdhci, 1, 1, 1); + +#ifndef MMCCAM MMC_DECLARE_BRIDGE(sdhci_ti); +#endif diff --git a/freebsd/sys/cam/cam.h b/freebsd/sys/cam/cam.h index 23feb508..4bd569be 100644 --- a/freebsd/sys/cam/cam.h +++ b/freebsd/sys/cam/cam.h @@ -32,11 +32,7 @@ #define _CAM_CAM_H 1 #ifdef _KERNEL -#ifndef __rtems__ -#include <opt_cam.h> -#else /* __rtems__ */ #include <rtems/bsd/local/opt_cam.h> -#endif /* __rtems__ */ #endif #include <sys/cdefs.h> diff --git a/freebsd/sys/cam/cam_ccb.h b/freebsd/sys/cam/cam_ccb.h index 99249f43..92d76d6a 100644 --- a/freebsd/sys/cam/cam_ccb.h +++ b/freebsd/sys/cam/cam_ccb.h @@ -42,6 +42,7 @@ #include <cam/scsi/scsi_all.h> #include <cam/ata/ata_all.h> #include <cam/nvme/nvme_all.h> +#include <cam/mmc/mmc_all.h> #ifdef __rtems__ #include <rtems/blkdev.h> #endif /* __rtems__ */ @@ -209,15 +210,18 @@ typedef enum { /* Serial Management Protocol */ XPT_NVME_IO = 0x1c | XPT_FC_DEV_QUEUED, - /* Execiute the requestred NVMe I/O operation */ + /* Execute the requested NVMe I/O operation */ - XPT_MMCSD_IO = 0x1d | XPT_FC_DEV_QUEUED, + XPT_MMC_IO = 0x1d | XPT_FC_DEV_QUEUED, /* Placeholder for MMC / SD / SDIO I/O stuff */ - XPT_SCAN_TGT = 0x1E | XPT_FC_QUEUED | XPT_FC_USER_CCB + XPT_SCAN_TGT = 0x1e | XPT_FC_QUEUED | XPT_FC_USER_CCB | XPT_FC_XPT_ONLY, /* Scan Target */ + XPT_NVME_ADMIN = 0x1f | XPT_FC_DEV_QUEUED, + /* Execute the requested NVMe Admin operation */ + /* HBA engine commands 0x20->0x2F */ XPT_ENG_INQ = 0x20 | XPT_FC_XPT_ONLY, /* HBA engine feature inquiry */ @@ -270,6 +274,7 @@ typedef enum { PROTO_SATAPM, /* SATA Port Multiplier */ PROTO_SEMB, /* SATA Enclosure Management Bridge */ PROTO_NVME, /* NVME */ + PROTO_MMCSD, /* MMC, SD, SDIO */ } cam_proto; typedef enum { @@ -286,6 +291,7 @@ typedef enum { XPORT_ISCSI, /* iSCSI */ XPORT_SRP, /* SCSI RDMA Protocol */ XPORT_NVME, /* NVMe over PCIe */ + XPORT_MMCSD, /* MMC, SD, SDIO card */ } cam_xport; #define XPORT_IS_NVME(t) ((t) == XPORT_NVME) @@ -334,8 +340,8 @@ typedef struct { struct ccb_hdr { #ifndef __rtems__ cam_pinfo pinfo; /* Info for priority scheduling */ - camq_entry xpt_links; /* For chaining in the XPT layer */ - camq_entry sim_links; /* For chaining in the SIM layer */ + camq_entry xpt_links; /* For chaining in the XPT layer */ + camq_entry sim_links; /* For chaining in the SIM layer */ camq_entry periph_links; /* For chaining in the type driver */ #else /* __rtems__ */ struct cam_sim *sim; @@ -380,7 +386,7 @@ struct ccb_getdev { /* Device Statistics CCB */ struct ccb_getdevstats { struct ccb_hdr ccb_h; - int dev_openings; /* Space left for more work on device*/ + int dev_openings; /* Space left for more work on device*/ int dev_active; /* Transactions running on the device */ int allocated; /* CCBs allocated for the device */ int queued; /* CCBs queued to be sent to the device */ @@ -454,7 +460,7 @@ struct device_match_pattern { union { struct scsi_static_inquiry_pattern inq_pat; struct device_id_match_pattern devid_pat; - } data; + } data; }; typedef enum { @@ -512,6 +518,7 @@ struct device_match_result { struct scsi_inquiry_data inq_data; struct ata_params ident_data; dev_result_flags flags; + struct mmc_params mmc_ident_data; }; struct bus_match_result { @@ -554,7 +561,7 @@ typedef enum { struct ccb_dm_cookie { void *bus; - void *target; + void *target; void *device; void *periph; void *pdrv; @@ -737,7 +744,7 @@ struct ccb_scsiio { u_int8_t *req_map; /* Ptr to mapping info */ u_int8_t *data_ptr; /* Ptr to the data buf/SG list */ u_int32_t dxfer_len; /* Data transfer length */ - /* Autosense storage */ + /* Autosense storage */ struct scsi_sense_data sense_data; u_int8_t sense_len; /* Number of bytes to autosense */ u_int8_t cdb_len; /* Number of bytes for the CDB */ @@ -792,6 +799,16 @@ struct ccb_ataio { uint32_t unused; }; +/* + * MMC I/O Request CCB used for the XPT_MMC_IO function code. + */ +struct ccb_mmcio { + struct ccb_hdr ccb_h; + union ccb *next_ccb; /* Ptr for next CCB for action */ + struct mmc_command cmd; + struct mmc_command stop; +}; + struct ccb_accept_tio { struct ccb_hdr ccb_h; cdb_t cdb_io; /* Union for CDB bytes/pointer */ @@ -824,7 +841,7 @@ struct ccb_relsim { }; /* - * NVMe I/O Request CCB used for the XPT_NVME_IO function code. + * NVMe I/O Request CCB used for the XPT_NVME_IO and XPT_NVME_ADMIN function codes. */ struct ccb_nvmeio { struct ccb_hdr ccb_h; @@ -886,7 +903,7 @@ struct ac_device_changed { /* Set Asynchronous Callback CCB */ struct ccb_setasync { struct ccb_hdr ccb_h; - u_int32_t event_enable; /* Async Event enables */ + u_int32_t event_enable; /* Async Event enables */ ac_callback_t *callback; void *callback_arg; }; @@ -1028,7 +1045,28 @@ struct ccb_trans_settings_nvme u_int max_xfer; /* Max transfer size (0 -> unlimited */ u_int caps; }; - + +#include <cam/mmc/mmc_bus.h> +struct ccb_trans_settings_mmc { + struct mmc_ios ios; +#define MMC_CLK (1 << 1) +#define MMC_VDD (1 << 2) +#define MMC_CS (1 << 3) +#define MMC_BW (1 << 4) +#define MMC_PM (1 << 5) +#define MMC_BT (1 << 6) +#define MMC_BM (1 << 7) + uint32_t ios_valid; +/* The folowing is used only for GET_TRAN_SETTINGS */ + uint32_t host_ocr; + int host_f_min; + int host_f_max; +#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can do 4-bit data transfers */ +#define MMC_CAP_8_BIT_DATA (1 << 1) /* Can do 8-bit data transfers */ +#define MMC_CAP_HSPEED (1 << 2) /* Can do High Speed transfers */ + uint32_t host_caps; +}; + /* Get/Set transfer rate/width/disconnection/tag queueing settings */ struct ccb_trans_settings { struct ccb_hdr ccb_h; @@ -1042,6 +1080,7 @@ struct ccb_trans_settings { struct ccb_trans_settings_ata ata; struct ccb_trans_settings_scsi scsi; struct ccb_trans_settings_nvme nvme; + struct ccb_trans_settings_mmc mmc; } proto_specific; union { u_int valid; /* Which fields to honor */ @@ -1063,7 +1102,7 @@ struct ccb_calc_geometry { struct ccb_hdr ccb_h; u_int32_t block_size; u_int64_t volume_size; - u_int32_t cylinders; + u_int32_t cylinders; u_int8_t heads; u_int8_t secs_per_track; }; @@ -1285,8 +1324,8 @@ union ccb { struct ccb_getdevstats cgds; struct ccb_dev_match cdm; struct ccb_trans_settings cts; - struct ccb_calc_geometry ccg; - struct ccb_sim_knob knob; + struct ccb_calc_geometry ccg; + struct ccb_sim_knob knob; struct ccb_abort cab; struct ccb_resetbus crb; struct ccb_resetdev crd; @@ -1307,6 +1346,7 @@ union ccb { struct ccb_dev_advinfo cdai; struct ccb_async casync; struct ccb_nvmeio nvmeio; + struct ccb_mmcio mmcio; }; #define CCB_CLEAR_ALL_EXCEPT_HDR(ccbp) \ @@ -1343,13 +1383,20 @@ cam_fill_ataio(struct ccb_ataio *ataio, u_int32_t retries, u_int32_t timeout); static __inline void -cam_fill_smpio(struct ccb_smpio *smpio, uint32_t retries, +cam_fill_smpio(struct ccb_smpio *smpio, uint32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint32_t flags, uint8_t *smp_request, int smp_request_len, uint8_t *smp_response, int smp_response_len, uint32_t timeout); static __inline void +cam_fill_mmcio(struct ccb_mmcio *mmcio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), uint32_t flags, + uint32_t mmc_opcode, uint32_t mmc_arg, uint32_t mmc_flags, + struct mmc_data *mmc_d, + uint32_t timeout); + +static __inline void cam_fill_csio(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int32_t flags, u_int8_t tag_action, @@ -1360,7 +1407,7 @@ cam_fill_csio(struct ccb_scsiio *csio, u_int32_t retries, csio->ccb_h.func_code = XPT_SCSI_IO; csio->ccb_h.flags = flags; csio->ccb_h.xflags = 0; - csio->ccb_h.retry_count = retries; + csio->ccb_h.retry_count = retries; csio->ccb_h.cbfcnp = cbfcnp; csio->ccb_h.timeout = timeout; csio->data_ptr = data_ptr; @@ -1383,7 +1430,7 @@ cam_fill_ctio(struct ccb_scsiio *csio, u_int32_t retries, csio->ccb_h.func_code = XPT_CONT_TARGET_IO; csio->ccb_h.flags = flags; csio->ccb_h.xflags = 0; - csio->ccb_h.retry_count = retries; + csio->ccb_h.retry_count = retries; csio->ccb_h.cbfcnp = cbfcnp; csio->ccb_h.timeout = timeout; csio->data_ptr = data_ptr; @@ -1412,7 +1459,7 @@ cam_fill_ataio(struct ccb_ataio *ataio, u_int32_t retries, } static __inline void -cam_fill_smpio(struct ccb_smpio *smpio, uint32_t retries, +cam_fill_smpio(struct ccb_smpio *smpio, uint32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint32_t flags, uint8_t *smp_request, int smp_request_len, uint8_t *smp_response, int smp_response_len, @@ -1438,6 +1485,34 @@ cam_fill_smpio(struct ccb_smpio *smpio, uint32_t retries, } static __inline void +cam_fill_mmcio(struct ccb_mmcio *mmcio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), uint32_t flags, + uint32_t mmc_opcode, uint32_t mmc_arg, uint32_t mmc_flags, + struct mmc_data *mmc_d, + uint32_t timeout) +{ + mmcio->ccb_h.func_code = XPT_MMC_IO; + mmcio->ccb_h.flags = flags; + mmcio->ccb_h.retry_count = retries; + mmcio->ccb_h.cbfcnp = cbfcnp; + mmcio->ccb_h.timeout = timeout; + mmcio->cmd.opcode = mmc_opcode; + mmcio->cmd.arg = mmc_arg; + mmcio->cmd.flags = mmc_flags; + mmcio->stop.opcode = 0; + mmcio->stop.arg = 0; + mmcio->stop.flags = 0; + if (mmc_d != NULL) { + mmcio->cmd.data = mmc_d; + } else + mmcio->cmd.data = NULL; + mmcio->cmd.resp[0] = 0; + mmcio->cmd.resp[1] = 0; + mmcio->cmd.resp[2] = 0; + mmcio->cmd.resp[3] = 0; +} + +static __inline void cam_set_ccbstatus(union ccb *ccb, cam_status status) { ccb->ccb_h.status &= ~CAM_STATUS_MASK; @@ -1466,6 +1541,21 @@ cam_fill_nvmeio(struct ccb_nvmeio *nvmeio, u_int32_t retries, nvmeio->data_ptr = data_ptr; nvmeio->dxfer_len = dxfer_len; } + +static __inline void +cam_fill_nvmeadmin(struct ccb_nvmeio *nvmeio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int32_t flags, u_int8_t *data_ptr, u_int32_t dxfer_len, + u_int32_t timeout) +{ + nvmeio->ccb_h.func_code = XPT_NVME_ADMIN; + nvmeio->ccb_h.flags = flags; + nvmeio->ccb_h.retry_count = retries; + nvmeio->ccb_h.cbfcnp = cbfcnp; + nvmeio->ccb_h.timeout = timeout; + nvmeio->data_ptr = data_ptr; + nvmeio->dxfer_len = dxfer_len; +} __END_DECLS #endif /* _CAM_CAM_CCB_H */ diff --git a/freebsd/sys/cam/cam_xpt.h b/freebsd/sys/cam/cam_xpt.h index 8e6027e5..47fdbd74 100644 --- a/freebsd/sys/cam/cam_xpt.h +++ b/freebsd/sys/cam/cam_xpt.h @@ -141,6 +141,8 @@ void xpt_copy_path(struct cam_path *new_path, void xpt_release_path(struct cam_path *path); +const char * xpt_action_name(uint32_t action); + #endif /* _KERNEL */ #endif /* _CAM_CAM_XPT_H */ diff --git a/freebsd/sys/cam/mmc/mmc.h b/freebsd/sys/cam/mmc/mmc.h new file mode 100644 index 00000000..9fae837e --- /dev/null +++ b/freebsd/sys/cam/mmc/mmc.h @@ -0,0 +1,94 @@ +/*- + * Copyright (c) 2014-2016 Ilya Bakulin. 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 ``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. + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * Inspired coded in sys/dev/mmc. Thanks to Warner Losh <imp@FreeBSD.org>, + * Bernd Walter <tisco@FreeBSD.org>, and other authors. + * + * $FreeBSD$ + */ + +#ifndef CAM_MMC_H +#define CAM_MMC_H + +#include <dev/mmc/mmcreg.h> +/* + * This structure describes an MMC/SD card + */ +struct mmc_params { + u_int8_t model[40]; /* Card model */ + + /* Card OCR */ + uint32_t card_ocr; + + /* OCR of the IO portion of the card */ + uint32_t io_ocr; + + /* Card CID -- raw and parsed */ + uint32_t card_cid[4]; + struct mmc_cid cid; + + /* Card CSD -- raw */ + uint32_t card_csd[4]; + + /* Card RCA */ + uint16_t card_rca; + + /* What kind of card is it */ + uint32_t card_features; +#define CARD_FEATURE_MEMORY 0x1 +#define CARD_FEATURE_SDHC 0x1 << 1 +#define CARD_FEATURE_SDIO 0x1 << 2 +#define CARD_FEATURE_SD20 0x1 << 3 +#define CARD_FEATURE_MMC 0x1 << 4 + + uint8_t sdio_func_count; +} __packed; + +#endif diff --git a/freebsd/sys/cam/mmc/mmc_all.h b/freebsd/sys/cam/mmc/mmc_all.h new file mode 100644 index 00000000..c2494894 --- /dev/null +++ b/freebsd/sys/cam/mmc/mmc_all.h @@ -0,0 +1,70 @@ +/*- + * Copyright (c) 2014-2016 Ilya Bakulin. 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 ``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. + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * $FreeBSD$ + */ + +/* + * MMC function that should be visible to the CAM subsystem + * and are somehow useful should be declared here + * + * Like in other *_all.h, it's also a nice place to include + * some other transport-specific headers. + */ + +#ifndef CAM_MMC_ALL_H +#define CAM_MMC_ALL_H + +#include <cam/mmc/mmc.h> +#include <dev/mmc/mmcreg.h> + +void mmc_print_ident(struct mmc_params *ident_data); + +#endif diff --git a/freebsd/sys/cam/mmc/mmc_bus.h b/freebsd/sys/cam/mmc/mmc_bus.h new file mode 100644 index 00000000..db77da51 --- /dev/null +++ b/freebsd/sys/cam/mmc/mmc_bus.h @@ -0,0 +1,5 @@ +/* + * This file is in the public domain. + * $FreeBSD$ + */ +#include <dev/mmc/bridge.h> diff --git a/freebsd/sys/cam/mmc/mmc_sdio.h b/freebsd/sys/cam/mmc/mmc_sdio.h new file mode 100644 index 00000000..6d22ffc0 --- /dev/null +++ b/freebsd/sys/cam/mmc/mmc_sdio.h @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2014 Ilya Bakulin. 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 ``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. + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * $FreeBSD$ + */ + +/* + * Various SDIO-related stuff + */ + +#ifndef CAM_MMC_SDIO_H +#define CAM_MMC_SDIO_H + +void sdio_print_stupid_message(struct cam_periph *periph); +void sdio_fill_mmcio_rw_direct(union ccb *ccb, uint8_t f, uint8_t wr, uint32_t adr, uint8_t *data); +uint8_t sdio_parse_mmcio_rw_direct(union ccb *ccb, uint8_t *data); +#endif diff --git a/freebsd/sys/cam/scsi/scsi_all.c b/freebsd/sys/cam/scsi/scsi_all.c index 99f6c37d..85e95cb6 100644 --- a/freebsd/sys/cam/scsi/scsi_all.c +++ b/freebsd/sys/cam/scsi/scsi_all.c @@ -37,11 +37,7 @@ __FBSDID("$FreeBSD$"); #include <sys/stdint.h> #ifdef _KERNEL -#ifndef __rtems__ -#include <opt_scsi.h> -#else /* __rtems__ */ #include <rtems/bsd/local/opt_scsi.h> -#endif /* __rtems__ */ #include <sys/systm.h> #include <sys/libkern.h> diff --git a/freebsd/sys/dev/e1000/if_em.c b/freebsd/sys/dev/e1000/if_em.c index 69381438..f5a0b94e 100644 --- a/freebsd/sys/dev/e1000/if_em.c +++ b/freebsd/sys/dev/e1000/if_em.c @@ -205,7 +205,7 @@ static pci_vendor_info_t igb_vendor_info_array[] = PVID(0x8086, E1000_DEV_ID_I210_COPPER_OEM1, "Intel(R) PRO/1000 PCI-Express Network Driver"), PVID(0x8086, E1000_DEV_ID_I210_COPPER_FLASHLESS, "Intel(R) PRO/1000 PCI-Express Network Driver"), PVID(0x8086, E1000_DEV_ID_I210_SERDES_FLASHLESS, "Intel(R) PRO/1000 PCI-Express Network Driver"), - PVID(0x8086, E1000_DEV_ID_I210_FIBER, "Intel(R) PRO/1000 PCI-Express Network Driver"), + PVID(0x8086, E1000_DEV_ID_I210_FIBER, "Intel(R) PRO/1000 PCI-Express Network Driver"), PVID(0x8086, E1000_DEV_ID_I210_SERDES, "Intel(R) PRO/1000 PCI-Express Network Driver"), PVID(0x8086, E1000_DEV_ID_I210_SGMII, "Intel(R) PRO/1000 PCI-Express Network Driver"), PVID(0x8086, E1000_DEV_ID_I211_COPPER, "Intel(R) PRO/1000 PCI-Express Network Driver"), @@ -233,8 +233,8 @@ static int em_if_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs static void em_if_queues_free(if_ctx_t ctx); static uint64_t em_if_get_counter(if_ctx_t, ift_counter); -static void em_if_init(if_ctx_t ctx); -static void em_if_stop(if_ctx_t ctx); +static void em_if_init(if_ctx_t ctx); +static void em_if_stop(if_ctx_t ctx); static void em_if_media_status(if_ctx_t, struct ifmediareq *); static int em_if_media_change(if_ctx_t ctx); static int em_if_mtu_set(if_ctx_t ctx, uint32_t mtu); @@ -359,11 +359,11 @@ static device_method_t em_if_methods[] = { DEVMETHOD(ifdi_detach, em_if_detach), DEVMETHOD(ifdi_shutdown, em_if_shutdown), DEVMETHOD(ifdi_suspend, em_if_suspend), - DEVMETHOD(ifdi_resume, em_if_resume), + DEVMETHOD(ifdi_resume, em_if_resume), DEVMETHOD(ifdi_init, em_if_init), DEVMETHOD(ifdi_stop, em_if_stop), DEVMETHOD(ifdi_msix_intr_assign, em_if_msix_intr_assign), - DEVMETHOD(ifdi_intr_enable, em_if_enable_intr), + DEVMETHOD(ifdi_intr_enable, em_if_enable_intr), DEVMETHOD(ifdi_intr_disable, em_if_disable_intr), DEVMETHOD(ifdi_tx_queues_alloc, em_if_tx_queues_alloc), DEVMETHOD(ifdi_rx_queues_alloc, em_if_rx_queues_alloc), @@ -1027,7 +1027,7 @@ em_if_attach_post(if_ctx_t ctx) /* Non-AMT based hardware can now take control from firmware */ if (adapter->has_manage && !adapter->has_amt) em_get_hw_control(adapter); - + INIT_DEBUGOUT("em_if_attach_post: end"); return (error); @@ -1403,7 +1403,7 @@ em_msix_link(void *arg) u32 reg_icr; ++adapter->link_irq; - MPASS(adapter->hw.back != NULL); + MPASS(adapter->hw.back != NULL); reg_icr = E1000_READ_REG(&adapter->hw, E1000_ICR); if (reg_icr & E1000_ICR_RXO) @@ -1665,9 +1665,7 @@ em_if_timer(if_ctx_t ctx, uint16_t qid) if (qid != 0) return; - em_if_update_admin_status(ctx); - em_update_stats_counters(adapter); - + iflib_admin_intr_deferred(ctx); /* Reset LAA into RAR[0] on 82571 */ if ((adapter->hw.mac.type == e1000_82571) && e1000_get_laa_state_82571(&adapter->hw)) @@ -1693,8 +1691,9 @@ em_if_update_admin_status(if_ctx_t ctx) struct e1000_hw *hw = &adapter->hw; struct ifnet *ifp = iflib_get_ifp(ctx); device_t dev = iflib_get_dev(ctx); - u32 link_check = 0; + u32 link_check, thstat, ctrl; + link_check = thstat = ctrl = 0; /* Get the cached link value or read phy for real */ switch (hw->phy.media_type) { case e1000_media_type_copper: @@ -1719,11 +1718,21 @@ em_if_update_admin_status(if_ctx_t ctx) e1000_check_for_link(hw); link_check = adapter->hw.mac.serdes_has_link; break; - default: + /* VF device is type_unknown */ case e1000_media_type_unknown: + e1000_check_for_link(hw); + link_check = !hw->mac.get_link_status; + /* FALLTHROUGH */ + default: break; } + /* Check for thermal downshift or shutdown */ + if (hw->mac.type == e1000_i350) { + thstat = E1000_READ_REG(hw, E1000_THSTAT); + ctrl = E1000_READ_REG(hw, E1000_CTRL_EXT); + } + /* Now check for a transition */ if (link_check && (adapter->link_active == 0)) { e1000_get_speed_and_duplex(hw, &adapter->link_speed, @@ -1745,6 +1754,21 @@ em_if_update_admin_status(if_ctx_t ctx) adapter->link_active = 1; adapter->smartspeed = 0; if_setbaudrate(ifp, adapter->link_speed * 1000000); + if ((ctrl & E1000_CTRL_EXT_LINK_MODE_GMII) && + (thstat & E1000_THSTAT_LINK_THROTTLE)) + device_printf(dev, "Link: thermal downshift\n"); + /* Delay Link Up for Phy update */ + if (((hw->mac.type == e1000_i210) || + (hw->mac.type == e1000_i211)) && + (hw->phy.id == I210_I_PHY_ID)) + msec_delay(I210_LINK_DELAY); + /* Reset if the media type changed. */ + if ((hw->dev_spec._82575.media_changed) && + (adapter->hw.mac.type >= igb_mac_min)) { + hw->dev_spec._82575.media_changed = false; + adapter->flags |= IGB_MEDIA_RESET; + em_reset(ctx); + } iflib_link_state_change(ctx, LINK_STATE_UP, ifp->if_baudrate); printf("Link state changed to up\n"); } else if (!link_check && (adapter->link_active == 1)) { @@ -1757,6 +1781,7 @@ em_if_update_admin_status(if_ctx_t ctx) iflib_link_state_change(ctx, LINK_STATE_DOWN, ifp->if_baudrate); printf("link state changed to down\n"); } + em_update_stats_counters(adapter); E1000_WRITE_REG(&adapter->hw, E1000_IMS, EM_MSIX_LINK | E1000_IMS_LSC); } @@ -2212,6 +2237,114 @@ lem_smartspeed(struct adapter *adapter) adapter->smartspeed = 0; } +/********************************************************************* + * + * Initialize the DMA Coalescing feature + * + **********************************************************************/ +static void +igb_init_dmac(struct adapter *adapter, u32 pba) +{ + device_t dev = adapter->dev; + struct e1000_hw *hw = &adapter->hw; + u32 dmac, reg = ~E1000_DMACR_DMAC_EN; + u16 hwm; + u16 max_frame_size; + + if (hw->mac.type == e1000_i211) + return; + + max_frame_size = adapter->shared->isc_max_frame_size; + if (hw->mac.type > e1000_82580) { + + if (adapter->dmac == 0) { /* Disabling it */ + E1000_WRITE_REG(hw, E1000_DMACR, reg); + return; + } else + device_printf(dev, "DMA Coalescing enabled\n"); + + /* Set starting threshold */ + E1000_WRITE_REG(hw, E1000_DMCTXTH, 0); + + hwm = 64 * pba - max_frame_size / 16; + if (hwm < 64 * (pba - 6)) + hwm = 64 * (pba - 6); + reg = E1000_READ_REG(hw, E1000_FCRTC); + reg &= ~E1000_FCRTC_RTH_COAL_MASK; + reg |= ((hwm << E1000_FCRTC_RTH_COAL_SHIFT) + & E1000_FCRTC_RTH_COAL_MASK); + E1000_WRITE_REG(hw, E1000_FCRTC, reg); + + + dmac = pba - max_frame_size / 512; + if (dmac < pba - 10) + dmac = pba - 10; + reg = E1000_READ_REG(hw, E1000_DMACR); + reg &= ~E1000_DMACR_DMACTHR_MASK; + reg = ((dmac << E1000_DMACR_DMACTHR_SHIFT) + & E1000_DMACR_DMACTHR_MASK); + + /* transition to L0x or L1 if available..*/ + reg |= (E1000_DMACR_DMAC_EN | E1000_DMACR_DMAC_LX_MASK); + + /* Check if status is 2.5Gb backplane connection + * before configuration of watchdog timer, which is + * in msec values in 12.8usec intervals + * watchdog timer= msec values in 32usec intervals + * for non 2.5Gb connection + */ + if (hw->mac.type == e1000_i354) { + int status = E1000_READ_REG(hw, E1000_STATUS); + if ((status & E1000_STATUS_2P5_SKU) && + (!(status & E1000_STATUS_2P5_SKU_OVER))) + reg |= ((adapter->dmac * 5) >> 6); + else + reg |= (adapter->dmac >> 5); + } else { + reg |= (adapter->dmac >> 5); + } + + E1000_WRITE_REG(hw, E1000_DMACR, reg); + + E1000_WRITE_REG(hw, E1000_DMCRTRH, 0); + + /* Set the interval before transition */ + reg = E1000_READ_REG(hw, E1000_DMCTLX); + if (hw->mac.type == e1000_i350) + reg |= IGB_DMCTLX_DCFLUSH_DIS; + /* + ** in 2.5Gb connection, TTLX unit is 0.4 usec + ** which is 0x4*2 = 0xA. But delay is still 4 usec + */ + if (hw->mac.type == e1000_i354) { + int status = E1000_READ_REG(hw, E1000_STATUS); + if ((status & E1000_STATUS_2P5_SKU) && + (!(status & E1000_STATUS_2P5_SKU_OVER))) + reg |= 0xA; + else + reg |= 0x4; + } else { + reg |= 0x4; + } + + E1000_WRITE_REG(hw, E1000_DMCTLX, reg); + + /* free space in tx packet buffer to wake from DMA coal */ + E1000_WRITE_REG(hw, E1000_DMCTXTH, (IGB_TXPBSIZE - + (2 * max_frame_size)) >> 6); + + /* make low power state decision controlled by DMA coal */ + reg = E1000_READ_REG(hw, E1000_PCIEMISC); + reg &= ~E1000_PCIEMISC_LX_DECISION; + E1000_WRITE_REG(hw, E1000_PCIEMISC, reg); + + } else if (hw->mac.type == e1000_82580) { + u32 reg = E1000_READ_REG(hw, E1000_PCIEMISC); + E1000_WRITE_REG(hw, E1000_PCIEMISC, + reg & ~E1000_PCIEMISC_LX_DECISION); + E1000_WRITE_REG(hw, E1000_DMACR, 0); + } +} static void em_reset(if_ctx_t ctx) @@ -2224,6 +2357,8 @@ em_reset(if_ctx_t ctx) u32 pba; INIT_DEBUGOUT("em_reset: begin"); + /* Let the firmware know the OS is in control */ + em_get_hw_control(adapter); /* Set up smart power down as default off on newer adapters. */ if (!em_smart_pwr_down && (hw->mac.type == e1000_82571 || @@ -2402,15 +2537,15 @@ em_reset(if_ctx_t ctx) case e1000_vfadapt_i350: /* 16-byte granularity */ hw->fc.low_water = hw->fc.high_water - 16; - break; - case e1000_ich9lan: - case e1000_ich10lan: + break; + case e1000_ich9lan: + case e1000_ich10lan: if (if_getmtu(ifp) > ETHERMTU) { hw->fc.high_water = 0x2800; hw->fc.low_water = hw->fc.high_water - 8; break; } - /* else fall thru */ + /* FALLTHROUGH */ default: if (hw->mac.type == e1000_80003es2lan) hw->fc.pause_time = 0xFFFF; @@ -2419,13 +2554,24 @@ em_reset(if_ctx_t ctx) /* Issue a global reset */ e1000_reset_hw(hw); - E1000_WRITE_REG(hw, E1000_WUFC, 0); - em_disable_aspm(adapter); + if (adapter->hw.mac.type >= igb_mac_min) { + E1000_WRITE_REG(hw, E1000_WUC, 0); + } else { + E1000_WRITE_REG(hw, E1000_WUFC, 0); + em_disable_aspm(adapter); + } + if (adapter->flags & IGB_MEDIA_RESET) { + e1000_setup_init_funcs(hw, TRUE); + e1000_get_bus_info(hw); + adapter->flags &= ~IGB_MEDIA_RESET; + } /* and a re-init */ if (e1000_init_hw(hw) < 0) { device_printf(dev, "Hardware Initialization Failed\n"); return; } + if (adapter->hw.mac.type >= igb_mac_min) + igb_init_dmac(adapter, pba); E1000_WRITE_REG(hw, E1000_VET, ETHERTYPE_VLAN); e1000_get_phy_info(hw); @@ -2466,7 +2612,7 @@ em_initialize_rss_mapping(struct adapter *adapter) for (i = 0; i < 32; ++i) E1000_WRITE_REG(hw, E1000_RETA(i), reta); - E1000_WRITE_REG(hw, E1000_MRQC, E1000_MRQC_RSS_ENABLE_2Q | + E1000_WRITE_REG(hw, E1000_MRQC, E1000_MRQC_RSS_ENABLE_2Q | E1000_MRQC_RSS_FIELD_IPV4_TCP | E1000_MRQC_RSS_FIELD_IPV4 | E1000_MRQC_RSS_FIELD_IPV6_TCP_EX | @@ -2553,8 +2699,7 @@ igb_initialize_rss_mapping(struct adapter *adapter) arc4rand(&rss_key, sizeof(rss_key), 0); #endif for (i = 0; i < 10; i++) - E1000_WRITE_REG_ARRAY(hw, - E1000_RSSRK(0), i, rss_key[i]); + E1000_WRITE_REG_ARRAY(hw, E1000_RSSRK(0), i, rss_key[i]); /* * Configure the RSS fields to hash upon. @@ -2621,7 +2766,7 @@ em_setup_interface(if_ctx_t ctx) /* Enable only WOL MAGIC by default */ if (adapter->wol) { if_setcapenablebit(ifp, IFCAP_WOL_MAGIC, - IFCAP_WOL_MCAST| IFCAP_WOL_UCAST); + IFCAP_WOL_MCAST| IFCAP_WOL_UCAST); } else { if_setcapenablebit(ifp, 0, IFCAP_WOL_MAGIC | IFCAP_WOL_MCAST| IFCAP_WOL_UCAST); @@ -2693,7 +2838,7 @@ em_if_tx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int ntxqs txr->tx_base = (struct e1000_tx_desc *)vaddrs[i*ntxqs]; txr->tx_paddr = paddrs[i*ntxqs]; } - + device_printf(iflib_get_dev(ctx), "allocated for %d tx_queues\n", adapter->tx_num_queues); return (0); fail: @@ -2718,7 +2863,7 @@ em_if_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int nrxqs adapter->rx_num_queues, M_DEVBUF, M_NOWAIT | M_ZERO))) { device_printf(iflib_get_dev(ctx), "Unable to allocate queue memory\n"); error = ENOMEM; - goto fail; + goto fail; } for (i = 0, que = adapter->rx_queues; i < nrxqsets; i++, que++) { @@ -2758,7 +2903,7 @@ em_if_queues_free(if_ctx_t ctx) txr->tx_rsq = NULL; } free(adapter->tx_queues, M_DEVBUF); - adapter->tx_queues = NULL; + adapter->tx_queues = NULL; } if (rx_que != NULL) { @@ -3033,7 +3178,7 @@ em_initialize_receive_unit(if_ctx_t ctx) u64 bus_addr = rxr->rx_paddr; #if 0 u32 rdt = adapter->rx_num_queues -1; /* default */ -#endif +#endif E1000_WRITE_REG(hw, E1000_RDLEN(i), scctx->isc_nrxd[0] * sizeof(union e1000_rx_desc_extended)); @@ -3088,7 +3233,7 @@ em_initialize_receive_unit(if_ctx_t ctx) srrctl |= 2048 >> E1000_SRRCTL_BSIZEPKT_SHIFT; rctl |= E1000_RCTL_SZ_2048; } - + /* * If TX flow control is disabled and there's >1 queue defined, * enable DROP. @@ -3126,7 +3271,7 @@ em_initialize_receive_unit(if_ctx_t ctx) rxdctl &= 0xFFF00000; rxdctl |= IGB_RX_PTHRESH; rxdctl |= IGB_RX_HTHRESH << 8; - rxdctl |= IGB_RX_WTHRESH << 16; + rxdctl |= IGB_RX_WTHRESH << 16; E1000_WRITE_REG(hw, E1000_RXDCTL(i), rxdctl); } } else if (adapter->hw.mac.type >= e1000_pch2lan) { @@ -3255,7 +3400,7 @@ em_if_disable_intr(if_ctx_t ctx) /* * Bit of a misnomer, what this really means is * to enable OS management of the system... aka - * to disable special hardware management features + * to disable special hardware management features */ static void em_init_manageability(struct adapter *adapter) @@ -3309,6 +3454,9 @@ em_get_hw_control(struct adapter *adapter) { u32 ctrl_ext, swsm; + if (adapter->vf_ifp) + return; + if (adapter->hw.mac.type == e1000_82573) { swsm = E1000_READ_REG(&adapter->hw, E1000_SWSM); E1000_WRITE_REG(&adapter->hw, E1000_SWSM, @@ -3319,7 +3467,6 @@ em_get_hw_control(struct adapter *adapter) ctrl_ext = E1000_READ_REG(&adapter->hw, E1000_CTRL_EXT); E1000_WRITE_REG(&adapter->hw, E1000_CTRL_EXT, ctrl_ext | E1000_CTRL_EXT_DRV_LOAD); - return; } /* @@ -3639,7 +3786,7 @@ static void em_if_led_func(if_ctx_t ctx, int onoff) { struct adapter *adapter = iflib_get_softc(ctx); - + if (onoff) { e1000_setup_led(&adapter->hw); e1000_led_on(&adapter->hw); @@ -3785,7 +3932,7 @@ static uint64_t em_if_get_counter(if_ctx_t ctx, ift_counter cnt) { struct adapter *adapter = iflib_get_softc(ctx); - struct ifnet *ifp = iflib_get_ifp(ctx); + struct ifnet *ifp = iflib_get_ifp(ctx); switch (cnt) { case IFCOUNTER_COLLISIONS: @@ -3822,7 +3969,7 @@ static void em_add_hw_stats(struct adapter *adapter) { device_t dev = iflib_get_dev(adapter->ctx); - struct em_tx_queue *tx_que = adapter->tx_queues; + struct em_tx_queue *tx_que = adapter->tx_queues; struct em_rx_queue *rx_que = adapter->rx_queues; struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); @@ -4067,7 +4214,7 @@ em_add_hw_stats(struct adapter *adapter) /* Interrupt Stats */ - int_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "interrupts", + int_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "interrupts", CTLFLAG_RD, NULL, "Interrupt Statistics"); int_list = SYSCTL_CHILDREN(int_node); @@ -4244,7 +4391,7 @@ em_set_flowcntl(SYSCTL_HANDLER_ARGS) case e1000_fc_none: adapter->hw.fc.requested_mode = input; adapter->fc = input; - break; + break; default: /* Do nothing */ return (error); @@ -4292,7 +4439,7 @@ em_sysctl_debug_info(SYSCTL_HANDLER_ARGS) if (result == 1) { adapter = (struct adapter *) arg1; em_print_debug_info(adapter); - } + } return (error); } diff --git a/freebsd/sys/dev/e1000/if_em.h b/freebsd/sys/dev/e1000/if_em.h index 79af551c..67d97e17 100644 --- a/freebsd/sys/dev/e1000/if_em.h +++ b/freebsd/sys/dev/e1000/if_em.h @@ -235,6 +235,27 @@ #define EM_EEPROM_APME 0x400; #define EM_82544_APME 0x0004; + +/* Support AutoMediaDetect for Marvell M88 PHY in i354 */ +#define IGB_MEDIA_RESET (1 << 0) + +/* Define the starting Interrupt rate per Queue */ +#define IGB_INTS_PER_SEC 8000 +#define IGB_DEFAULT_ITR ((1000000/IGB_INTS_PER_SEC) << 2) + +#define IGB_LINK_ITR 2000 +#define I210_LINK_DELAY 1000 + +#define IGB_MAX_SCATTER 40 +#define IGB_VFTA_SIZE 128 +#define IGB_BR_SIZE 4096 /* ring buf size */ +#define IGB_TSO_SIZE (65535 + sizeof(struct ether_vlan_header)) +#define IGB_TSO_SEG_SIZE 4096 /* Max dma segment size */ +#define IGB_TXPBSIZE 20408 +#define IGB_HDR_BUF 128 +#define IGB_PKTTYPE_MASK 0x0000FFF0 +#define IGB_DMCTLX_DCFLUSH_DIS 0x80000000 /* Disable DMA Coalesce Flush */ + /* * Driver state logic for the detection of a hung state * in hardware. Set TX_HUNG whenever a TX packet is used @@ -455,11 +476,11 @@ struct adapter { struct ifmedia *media; int msix; int if_flags; - int min_frame_size; int em_insert_vlan_header; u32 ims; bool in_detach; + u32 flags; /* Task for FAST handling */ struct grouptask link_task; @@ -514,6 +535,7 @@ struct adapter { unsigned long watchdog_events; struct e1000_hw_stats stats; + u16 vf_ifp; }; /******************************************************************************** diff --git a/freebsd/sys/dev/evdev/cdev.c b/freebsd/sys/dev/evdev/cdev.c index b08a2440..10f4e77e 100644 --- a/freebsd/sys/dev/evdev/cdev.c +++ b/freebsd/sys/dev/evdev/cdev.c @@ -2,7 +2,7 @@ /*- * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org> - * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@cicgroup.ru> + * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,24 +31,23 @@ #include <rtems/bsd/local/opt_evdev.h> -#include <sys/types.h> -#include <sys/bitstring.h> -#include <sys/systm.h> #include <sys/param.h> -#include <sys/kernel.h> +#include <sys/bitstring.h> #include <sys/conf.h> -#include <sys/uio.h> -#include <sys/proc.h> -#include <sys/poll.h> #include <sys/filio.h> #include <sys/fcntl.h> -#include <sys/selinfo.h> +#include <sys/kernel.h> #include <sys/malloc.h> +#include <sys/poll.h> +#include <sys/proc.h> +#include <sys/selinfo.h> +#include <sys/systm.h> #include <sys/time.h> +#include <sys/uio.h> -#include <dev/evdev/input.h> #include <dev/evdev/evdev.h> #include <dev/evdev/evdev_private.h> +#include <dev/evdev/input.h> #ifdef EVDEV_DEBUG #define debugf(client, fmt, args...) printf("evdev cdev: "fmt"\n", ##args) diff --git a/freebsd/sys/dev/evdev/evdev.c b/freebsd/sys/dev/evdev/evdev.c index 185e48f7..b3c786a5 100644 --- a/freebsd/sys/dev/evdev/evdev.c +++ b/freebsd/sys/dev/evdev/evdev.c @@ -2,7 +2,7 @@ /*- * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org> - * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@cicgroup.ru> + * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,19 +31,18 @@ #include <rtems/bsd/local/opt_evdev.h> -#include <sys/types.h> -#include <sys/systm.h> #include <sys/param.h> -#include <sys/kernel.h> -#include <sys/module.h> +#include <sys/bitstring.h> #include <sys/conf.h> +#include <sys/kernel.h> #include <sys/malloc.h> -#include <sys/bitstring.h> +#include <sys/module.h> #include <sys/sysctl.h> +#include <sys/systm.h> -#include <dev/evdev/input.h> #include <dev/evdev/evdev.h> #include <dev/evdev/evdev_private.h> +#include <dev/evdev/input.h> #ifdef EVDEV_DEBUG #define debugf(evdev, fmt, args...) printf("evdev: " fmt "\n", ##args) @@ -764,14 +763,11 @@ evdev_push_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, int32_t value) { - if (evdev->ev_lock_type != EV_LOCK_INTERNAL) - EVDEV_LOCK_ASSERT(evdev); - if (evdev_check_event(evdev, type, code, value) != 0) return (EINVAL); - if (evdev->ev_lock_type == EV_LOCK_INTERNAL) - EVDEV_LOCK(evdev); + EVDEV_ENTER(evdev); + evdev_modify_event(evdev, type, code, &value); if (type == EV_SYN && code == SYN_REPORT && bit_test(evdev->ev_flags, EVDEV_FLAG_MT_AUTOREL)) @@ -780,8 +776,8 @@ evdev_push_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, bit_test(evdev->ev_flags, EVDEV_FLAG_MT_STCOMPAT)) evdev_send_mt_compat(evdev); evdev_send_event(evdev, type, code, value); - if (evdev->ev_lock_type == EV_LOCK_INTERNAL) - EVDEV_UNLOCK(evdev); + + EVDEV_EXIT(evdev); return (0); } diff --git a/freebsd/sys/dev/evdev/evdev_mt.c b/freebsd/sys/dev/evdev/evdev_mt.c index 6412544b..d0eb48f4 100644 --- a/freebsd/sys/dev/evdev/evdev_mt.c +++ b/freebsd/sys/dev/evdev/evdev_mt.c @@ -1,7 +1,7 @@ #include <machine/rtems-bsd-kernel-space.h> /*- - * Copyright (c) 2016 Vladimir Kondratyev <wulf@cicgroup.ru> + * Copyright (c) 2016 Vladimir Kondratyev <wulf@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,14 +29,14 @@ */ #include <sys/param.h> -#include <sys/malloc.h> #include <sys/lock.h> +#include <sys/malloc.h> #include <sys/mutex.h> #include <sys/systm.h> -#include <dev/evdev/input.h> #include <dev/evdev/evdev.h> #include <dev/evdev/evdev_private.h> +#include <dev/evdev/input.h> #ifdef DEBUG #define debugf(fmt, args...) printf("evdev: " fmt "\n", ##args) @@ -226,13 +226,9 @@ void evdev_push_nfingers(struct evdev_dev *evdev, int32_t nfingers) { - if (evdev->ev_lock_type == EV_LOCK_INTERNAL) - EVDEV_LOCK(evdev); - else - EVDEV_LOCK_ASSERT(evdev); + EVDEV_ENTER(evdev); evdev_send_nfingers(evdev, nfingers); - if (evdev->ev_lock_type == EV_LOCK_INTERNAL) - EVDEV_UNLOCK(evdev); + EVDEV_EXIT(evdev); } void @@ -266,13 +262,9 @@ void evdev_push_mt_compat(struct evdev_dev *evdev) { - if (evdev->ev_lock_type == EV_LOCK_INTERNAL) - EVDEV_LOCK(evdev); - else - EVDEV_LOCK_ASSERT(evdev); + EVDEV_ENTER(evdev); evdev_send_mt_compat(evdev); - if (evdev->ev_lock_type == EV_LOCK_INTERNAL) - EVDEV_UNLOCK(evdev); + EVDEV_EXIT(evdev); } void diff --git a/freebsd/sys/dev/evdev/evdev_private.h b/freebsd/sys/dev/evdev/evdev_private.h index b3de1bf0..05206a9d 100644 --- a/freebsd/sys/dev/evdev/evdev_private.h +++ b/freebsd/sys/dev/evdev/evdev_private.h @@ -1,6 +1,6 @@ /*- * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org> - * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@cicgroup.ru> + * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,10 +31,11 @@ #define _DEV_EVDEV_EVDEV_PRIVATE_H #include <sys/bitstring.h> -#include <sys/queue.h> -#include <sys/malloc.h> #include <sys/kbio.h> +#include <sys/malloc.h> +#include <sys/queue.h> #include <sys/selinfo.h> + #include <dev/evdev/evdev.h> #include <dev/evdev/input.h> #include <dev/kbd/kbdreg.h> @@ -134,6 +135,16 @@ struct evdev_dev #define EVDEV_LOCK(evdev) mtx_lock((evdev)->ev_lock) #define EVDEV_UNLOCK(evdev) mtx_unlock((evdev)->ev_lock) #define EVDEV_LOCK_ASSERT(evdev) mtx_assert((evdev)->ev_lock, MA_OWNED) +#define EVDEV_ENTER(evdev) do { \ + if ((evdev)->ev_lock_type == EV_LOCK_INTERNAL) \ + EVDEV_LOCK(evdev); \ + else \ + EVDEV_LOCK_ASSERT(evdev); \ +} while (0) +#define EVDEV_EXIT(evdev) do { \ + if ((evdev)->ev_lock_type == EV_LOCK_INTERNAL) \ + EVDEV_UNLOCK(evdev); \ +} while (0) struct evdev_client { diff --git a/freebsd/sys/dev/evdev/evdev_utils.c b/freebsd/sys/dev/evdev/evdev_utils.c index 49e03b6a..caf81a46 100644 --- a/freebsd/sys/dev/evdev/evdev_utils.c +++ b/freebsd/sys/dev/evdev/evdev_utils.c @@ -2,7 +2,7 @@ /*- * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org> - * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@cicgroup.ru> + * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,17 +29,16 @@ * $FreeBSD$ */ -#include <sys/types.h> -#include <sys/systm.h> #include <sys/param.h> #include <sys/bus.h> -#include <sys/kernel.h> #include <sys/conf.h> -#include <sys/malloc.h> #include <sys/kbio.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/systm.h> -#include <dev/evdev/input.h> #include <dev/evdev/evdev.h> +#include <dev/evdev/input.h> #include <dev/kbd/kbdreg.h> diff --git a/freebsd/sys/dev/evdev/input-event-codes.h b/freebsd/sys/dev/evdev/input-event-codes.h index 78ba7d2e..cc1528f6 100644 --- a/freebsd/sys/dev/evdev/input-event-codes.h +++ b/freebsd/sys/dev/evdev/input-event-codes.h @@ -1,6 +1,6 @@ /*- * Copyright (c) 2016 Oleksandr Tymoshenko <gonzo@FreeBSD.org> - * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@cicgroup.ru> + * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/freebsd/sys/dev/evdev/input.h b/freebsd/sys/dev/evdev/input.h index 04638444..7639e0d6 100644 --- a/freebsd/sys/dev/evdev/input.h +++ b/freebsd/sys/dev/evdev/input.h @@ -1,6 +1,6 @@ /*- * Copyright (c) 2016 Oleksandr Tymoshenko <gonzo@FreeBSD.org> - * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@cicgroup.ru> + * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,8 +31,8 @@ #define _EVDEV_INPUT_H #ifndef __KERNEL__ -#include <sys/time.h> #include <sys/ioccom.h> +#include <sys/time.h> #include <sys/types.h> #endif diff --git a/freebsd/sys/dev/evdev/uinput.c b/freebsd/sys/dev/evdev/uinput.c index efebf02a..f1f812cc 100644 --- a/freebsd/sys/dev/evdev/uinput.c +++ b/freebsd/sys/dev/evdev/uinput.c @@ -2,7 +2,7 @@ /*- * Copyright (c) 2014 Jakub Wojciech Klama <jceel@FreeBSD.org> - * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@cicgroup.ru> + * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,25 +31,24 @@ #include <rtems/bsd/local/opt_evdev.h> -#include <sys/types.h> -#include <sys/systm.h> #include <sys/param.h> +#include <sys/conf.h> #include <sys/fcntl.h> #include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> #include <sys/module.h> -#include <sys/conf.h> -#include <sys/uio.h> -#include <sys/proc.h> #include <sys/poll.h> +#include <sys/proc.h> #include <sys/selinfo.h> -#include <sys/malloc.h> -#include <sys/lock.h> +#include <sys/systm.h> #include <sys/sx.h> +#include <sys/uio.h> -#include <dev/evdev/input.h> -#include <dev/evdev/uinput.h> #include <dev/evdev/evdev.h> #include <dev/evdev/evdev_private.h> +#include <dev/evdev/input.h> +#include <dev/evdev/uinput.h> #ifdef UINPUT_DEBUG #define debugf(state, fmt, args...) printf("uinput: " fmt "\n", ##args) diff --git a/freebsd/sys/dev/evdev/uinput.h b/freebsd/sys/dev/evdev/uinput.h index f1721e19..dd4b0a82 100644 --- a/freebsd/sys/dev/evdev/uinput.h +++ b/freebsd/sys/dev/evdev/uinput.h @@ -1,6 +1,6 @@ /*- * Copyright (c) 2016 Oleksandr Tymoshenko <gonzo@FreeBSD.org> - * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@cicgroup.ru> + * Copyright (c) 2015-2016 Vladimir Kondratyev <wulf@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/freebsd/sys/dev/ffec/if_ffec.c b/freebsd/sys/dev/ffec/if_ffec.c index b9a0d668..22b2cdfc 100644 --- a/freebsd/sys/dev/ffec/if_ffec.c +++ b/freebsd/sys/dev/ffec/if_ffec.c @@ -90,6 +90,7 @@ __FBSDID("$FreeBSD$"); #include <dev/ofw/ofw_bus_subr.h> #include <dev/mii/mii.h> #include <dev/mii/miivar.h> +#include <dev/mii/mii_fdt.h> #include <rtems/bsd/local/miibus_if.h> /* @@ -140,7 +141,6 @@ static struct ofw_compat_data compat_data[] = { #define TX_MAX_DMA_SEGS 8 #define WATCHDOG_TIMEOUT_SECS 5 -#define STATS_HARVEST_INTERVAL 3 #define MAX_IRQ_COUNT 3 @@ -153,13 +153,6 @@ struct ffec_bufmap { bus_dmamap_t map; }; -enum { - PHY_CONN_UNKNOWN, - PHY_CONN_MII, - PHY_CONN_RMII, - PHY_CONN_RGMII -}; - struct ffec_softc { device_t dev; device_t miibus; @@ -172,15 +165,14 @@ struct ffec_softc { struct resource *mem_res; void * intr_cookie[MAX_IRQ_COUNT]; struct callout ffec_callout; - uint8_t phy_conn_type; + mii_contype_t phy_conn_type; uintptr_t fectype; boolean_t link_is_up; boolean_t is_attached; boolean_t is_detaching; int tx_watchdog_count; - int stats_harvest_count; - int rxbuf_align; - int txbuf_align; + int rxbuf_align; + int txbuf_align; bus_dma_tag_t rxdesc_tag; bus_dmamap_t rxdesc_map; @@ -320,10 +312,10 @@ ffec_miigasket_setup(struct ffec_softc *sc) switch (sc->phy_conn_type) { - case PHY_CONN_MII: + case MII_CONTYPE_MII: ifmode = 0; break; - case PHY_CONN_RMII: + case MII_CONTYPE_RMII: ifmode = FEC_MIIGSK_CFGR_IF_MODE_RMII; break; default: @@ -435,14 +427,17 @@ ffec_miibus_statchg(device_t dev) rcr |= FEC_RCR_MII_MODE; /* Must always be on even for R[G]MII. */ switch (sc->phy_conn_type) { - case PHY_CONN_MII: - break; - case PHY_CONN_RMII: + case MII_CONTYPE_RMII: rcr |= FEC_RCR_RMII_MODE; break; - case PHY_CONN_RGMII: + case MII_CONTYPE_RGMII: + case MII_CONTYPE_RGMII_ID: + case MII_CONTYPE_RGMII_RXID: + case MII_CONTYPE_RGMII_TXID: rcr |= FEC_RCR_RGMII_EN; break; + default: + break; } switch (IFM_SUBTYPE(mii->mii_media_active)) { @@ -518,22 +513,41 @@ ffec_media_change(struct ifnet * ifp) static void ffec_clear_stats(struct ffec_softc *sc) { + uint32_t mibc; - WR4(sc, FEC_RMON_R_PACKETS, 0); - WR4(sc, FEC_RMON_R_MC_PKT, 0); - WR4(sc, FEC_RMON_R_CRC_ALIGN, 0); - WR4(sc, FEC_RMON_R_UNDERSIZE, 0); - WR4(sc, FEC_RMON_R_OVERSIZE, 0); - WR4(sc, FEC_RMON_R_FRAG, 0); - WR4(sc, FEC_RMON_R_JAB, 0); - WR4(sc, FEC_RMON_T_PACKETS, 0); - WR4(sc, FEC_RMON_T_MC_PKT, 0); - WR4(sc, FEC_RMON_T_CRC_ALIGN, 0); - WR4(sc, FEC_RMON_T_UNDERSIZE, 0); - WR4(sc, FEC_RMON_T_OVERSIZE , 0); - WR4(sc, FEC_RMON_T_FRAG, 0); - WR4(sc, FEC_RMON_T_JAB, 0); - WR4(sc, FEC_RMON_T_COL, 0); + mibc = RD4(sc, FEC_MIBC_REG); + + /* + * On newer hardware the statistic regs are cleared by toggling a bit in + * the mib control register. On older hardware the clear procedure is + * to disable statistics collection, zero the regs, then re-enable. + */ + if (sc->fectype == FECTYPE_IMX6 || sc->fectype == FECTYPE_MVF) { + WR4(sc, FEC_MIBC_REG, mibc | FEC_MIBC_CLEAR); + WR4(sc, FEC_MIBC_REG, mibc & ~FEC_MIBC_CLEAR); + } else { + WR4(sc, FEC_MIBC_REG, mibc | FEC_MIBC_DIS); + + WR4(sc, FEC_IEEE_R_DROP, 0); + WR4(sc, FEC_IEEE_R_MACERR, 0); + WR4(sc, FEC_RMON_R_CRC_ALIGN, 0); + WR4(sc, FEC_RMON_R_FRAG, 0); + WR4(sc, FEC_RMON_R_JAB, 0); + WR4(sc, FEC_RMON_R_MC_PKT, 0); + WR4(sc, FEC_RMON_R_OVERSIZE, 0); + WR4(sc, FEC_RMON_R_PACKETS, 0); + WR4(sc, FEC_RMON_R_UNDERSIZE, 0); + WR4(sc, FEC_RMON_T_COL, 0); + WR4(sc, FEC_RMON_T_CRC_ALIGN, 0); + WR4(sc, FEC_RMON_T_FRAG, 0); + WR4(sc, FEC_RMON_T_JAB, 0); + WR4(sc, FEC_RMON_T_MC_PKT, 0); + WR4(sc, FEC_RMON_T_OVERSIZE , 0); + WR4(sc, FEC_RMON_T_PACKETS, 0); + WR4(sc, FEC_RMON_T_UNDERSIZE, 0); + + WR4(sc, FEC_MIBC_REG, mibc); + } } static void @@ -541,28 +555,21 @@ ffec_harvest_stats(struct ffec_softc *sc) { struct ifnet *ifp; - /* We don't need to harvest too often. */ - if (++sc->stats_harvest_count < STATS_HARVEST_INTERVAL) - return; + ifp = sc->ifp; /* - * Try to avoid harvesting unless the IDLE flag is on, but if it has - * been too long just go ahead and do it anyway, the worst that'll - * happen is we'll lose a packet count or two as we clear at the end. + * - FEC_IEEE_R_DROP is "dropped due to invalid start frame delimiter" + * so it's really just another type of input error. + * - FEC_IEEE_R_MACERR is "no receive fifo space"; count as input drops. */ - if (sc->stats_harvest_count < (2 * STATS_HARVEST_INTERVAL) && - ((RD4(sc, FEC_MIBC_REG) & FEC_MIBC_IDLE) == 0)) - return; - - sc->stats_harvest_count = 0; - ifp = sc->ifp; - if_inc_counter(ifp, IFCOUNTER_IPACKETS, RD4(sc, FEC_RMON_R_PACKETS)); if_inc_counter(ifp, IFCOUNTER_IMCASTS, RD4(sc, FEC_RMON_R_MC_PKT)); if_inc_counter(ifp, IFCOUNTER_IERRORS, RD4(sc, FEC_RMON_R_CRC_ALIGN) + RD4(sc, FEC_RMON_R_UNDERSIZE) + RD4(sc, FEC_RMON_R_OVERSIZE) + RD4(sc, FEC_RMON_R_FRAG) + - RD4(sc, FEC_RMON_R_JAB)); + RD4(sc, FEC_RMON_R_JAB) + RD4(sc, FEC_IEEE_R_DROP)); + + if_inc_counter(ifp, IFCOUNTER_IQDROPS, RD4(sc, FEC_IEEE_R_MACERR)); if_inc_counter(ifp, IFCOUNTER_OPACKETS, RD4(sc, FEC_RMON_T_PACKETS)); if_inc_counter(ifp, IFCOUNTER_OMCASTS, RD4(sc, FEC_RMON_T_MC_PKT)); @@ -1132,7 +1139,6 @@ ffec_stop_locked(struct ffec_softc *sc) ifp = sc->ifp; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); sc->tx_watchdog_count = 0; - sc->stats_harvest_count = 0; /* * Stop the hardware, mask all interrupts, and clear all current @@ -1311,7 +1317,8 @@ ffec_init_locked(struct ffec_softc *sc) WR4(sc, FEC_IEM_REG, FEC_IER_TXF | FEC_IER_RXF | FEC_IER_EBERR); /* - * MIBC - MIB control (hardware stats). + * MIBC - MIB control (hardware stats); clear all statistics regs, then + * enable collection of statistics. */ regval = RD4(sc, FEC_MIBC_REG); WR4(sc, FEC_MIBC_REG, regval | FEC_MIBC_DIS); @@ -1708,7 +1715,6 @@ ffec_attach(device_t dev) phandle_t ofw_node; int error, phynum, rid, irq; uint8_t eaddr[ETHER_ADDR_LEN]; - char phy_conn_name[32]; uint32_t idx, mscr; sc = device_get_softc(dev); @@ -1739,20 +1745,8 @@ ffec_attach(device_t dev) error = ENXIO; goto out; } - if (OF_searchprop(ofw_node, "phy-mode", - phy_conn_name, sizeof(phy_conn_name)) != -1) { - if (strcasecmp(phy_conn_name, "mii") == 0) - sc->phy_conn_type = PHY_CONN_MII; - else if (strcasecmp(phy_conn_name, "rmii") == 0) - sc->phy_conn_type = PHY_CONN_RMII; -#ifndef __rtems__ - else if (strcasecmp(phy_conn_name, "rgmii") == 0) -#else /* __rtems__ */ - else if (strncasecmp(phy_conn_name, "rgmii", 5) == 0) -#endif /* __rtems__ */ - sc->phy_conn_type = PHY_CONN_RGMII; - } - if (sc->phy_conn_type == PHY_CONN_UNKNOWN) { + sc->phy_conn_type = mii_fdt_get_contype(ofw_node); + if (sc->phy_conn_type == MII_CONTYPE_UNKNOWN) { device_printf(sc->dev, "No valid 'phy-mode' " "property found in FDT data for device.\n"); #ifndef __rtems__ diff --git a/freebsd/sys/dev/mii/mii_fdt.c b/freebsd/sys/dev/mii/mii_fdt.c new file mode 100644 index 00000000..de994ff9 --- /dev/null +++ b/freebsd/sys/dev/mii/mii_fdt.c @@ -0,0 +1,202 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2017 Ian Lepore <ian@freebsd.org> + * All rights reserved. + * + * Development sponsored by Microsemi, Inc. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Utility functions for PHY drivers on systems configured using FDT data. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/socket.h> +#include <sys/bus.h> +#include <sys/malloc.h> + +#include <net/if.h> +#include <net/if_media.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> +#include <dev/mii/mii_fdt.h> + +/* + * Table to translate MII_CONTYPE_xxxx constants to/from devicetree strings. + * We explicitly associate the enum values with the strings in a table to avoid + * relying on this list being sorted in the same order as the enum in miivar.h, + * and to avoid problems if the enum gains new types that aren't in the FDT + * data. However, the "unknown" entry must be first because it is referenced + * using subscript 0 in mii_fdt_contype_to_name(). + */ +static struct contype_names { + mii_contype_t type; + const char *name; +} fdt_contype_names[] = { + {MII_CONTYPE_UNKNOWN, "unknown"}, + {MII_CONTYPE_MII, "mii"}, + {MII_CONTYPE_GMII, "gmii"}, + {MII_CONTYPE_SGMII, "sgmii"}, + {MII_CONTYPE_QSGMII, "qsgmii"}, + {MII_CONTYPE_TBI, "tbi"}, + {MII_CONTYPE_REVMII, "rev-mii"}, + {MII_CONTYPE_RMII, "rmii"}, + {MII_CONTYPE_RGMII, "rgmii"}, + {MII_CONTYPE_RGMII_ID, "rgmii-id"}, + {MII_CONTYPE_RGMII_RXID, "rgmii-rxid"}, + {MII_CONTYPE_RGMII_TXID, "rgmii-txid"}, + {MII_CONTYPE_RTBI, "rtbi"}, + {MII_CONTYPE_SMII, "smii"}, + {MII_CONTYPE_XGMII, "xgmii"}, + {MII_CONTYPE_TRGMII, "trgmii"}, + {MII_CONTYPE_2000BX, "2000base-x"}, + {MII_CONTYPE_2500BX, "2500base-x"}, + {MII_CONTYPE_RXAUI, "rxaui"}, +}; + +static phandle_t +mii_fdt_get_phynode(phandle_t macnode) +{ + static const char *props[] = { + "phy-handle", "phy", "phy-device" + }; + pcell_t xref; + u_int i; + + for (i = 0; i < nitems(props); ++i) { + if (OF_getencprop(macnode, props[i], &xref, sizeof(xref)) > 0) + return (OF_node_from_xref(xref)); + } + return (-1); +} + +mii_contype_t +mii_fdt_contype_from_name(const char *name) +{ + u_int i; + + for (i = 0; i < nitems(fdt_contype_names); ++i) { + if (strcmp(name, fdt_contype_names[i].name) == 0) + return (fdt_contype_names[i].type); + } + return (MII_CONTYPE_UNKNOWN); +} + +const char * +mii_fdt_contype_to_name(mii_contype_t contype) +{ + u_int i; + + for (i = 0; i < nitems(fdt_contype_names); ++i) { + if (contype == fdt_contype_names[i].type) + return (fdt_contype_names[i].name); + } + return (fdt_contype_names[0].name); +} + +mii_contype_t +mii_fdt_get_contype(phandle_t macnode) +{ + char val[32]; + + if (OF_getprop(macnode, "phy-mode", val, sizeof(val)) <= 0 && + OF_getprop(macnode, "phy-connection-type", val, sizeof(val)) <= 0) { + return (MII_CONTYPE_UNKNOWN); + } + return (mii_fdt_contype_from_name(val)); +} + +void +mii_fdt_free_config(struct mii_fdt_phy_config *cfg) +{ + + free(cfg, M_OFWPROP); +} + +mii_fdt_phy_config_t * +mii_fdt_get_config(device_t phydev) +{ + mii_fdt_phy_config_t *cfg; + device_t miibus, macdev; + pcell_t val; + + miibus = device_get_parent(phydev); + macdev = device_get_parent(miibus); + + cfg = malloc(sizeof(*cfg), M_OFWPROP, M_ZERO | M_WAITOK); + + /* + * If we can't find our parent MAC's node, there's nothing more we can + * fill in; cfg is already full of zero/default values, return it. + */ + if ((cfg->macnode = ofw_bus_get_node(macdev)) == -1) + return (cfg); + + cfg->con_type = mii_fdt_get_contype(cfg->macnode); + + /* + * If we can't find our own PHY node, there's nothing more we can fill + * in, just return what we've got. + */ + if ((cfg->phynode = mii_fdt_get_phynode(cfg->macnode)) == -1) + return (cfg); + + if (OF_getencprop(cfg->phynode, "max-speed", &val, sizeof(val)) > 0) + cfg->max_speed = val; + + if (ofw_bus_node_is_compatible(cfg->phynode, + "ethernet-phy-ieee802.3-c45")) + cfg->flags |= MIIF_FDT_COMPAT_CLAUSE45; + + if (OF_hasprop(cfg->phynode, "broken-turn-around")) + cfg->flags |= MIIF_FDT_BROKEN_TURNAROUND; + if (OF_hasprop(cfg->phynode, "enet-phy-lane-swap")) + cfg->flags |= MIIF_FDT_LANE_SWAP; + if (OF_hasprop(cfg->phynode, "enet-phy-lane-no-swap")) + cfg->flags |= MIIF_FDT_NO_LANE_SWAP; + if (OF_hasprop(cfg->phynode, "eee-broken-100tx")) + cfg->flags |= MIIF_FDT_EEE_BROKEN_100TX; + if (OF_hasprop(cfg->phynode, "eee-broken-1000t")) + cfg->flags |= MIIF_FDT_EEE_BROKEN_1000T; + if (OF_hasprop(cfg->phynode, "eee-broken-10gt")) + cfg->flags |= MIIF_FDT_EEE_BROKEN_10GT; + if (OF_hasprop(cfg->phynode, "eee-broken-1000kx")) + cfg->flags |= MIIF_FDT_EEE_BROKEN_1000KX; + if (OF_hasprop(cfg->phynode, "eee-broken-10gkx4")) + cfg->flags |= MIIF_FDT_EEE_BROKEN_10GKX4; + if (OF_hasprop(cfg->phynode, "eee-broken-10gkr")) + cfg->flags |= MIIF_FDT_EEE_BROKEN_10GKR; + + return (cfg); +} diff --git a/freebsd/sys/dev/mii/mii_fdt.h b/freebsd/sys/dev/mii/mii_fdt.h new file mode 100644 index 00000000..7b0b5131 --- /dev/null +++ b/freebsd/sys/dev/mii/mii_fdt.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 2017 Ian Lepore <ian@freebsd.org> + * All rights reserved. + * + * Development sponsored by Microsemi, Inc. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 _DEV_MII_FDT_H_ +#define _DEV_MII_FDT_H_ + +/* + * Common FDT config for a PHY, as documented in the devicetree bindings + * documents ethernet.txt and phy.txt. Boolean properties are represented as + * bits in the flags member. + */ +struct mii_fdt_phy_config { + phandle_t macnode; /* Node (not xref) of parent MAC */ + phandle_t phynode; /* Node (not xref) of PHY */ + mii_contype_t con_type; /* MAC<->PHY connection type */ + u_int max_speed; /* Mbits/sec, 0 = not specified */ + uint32_t flags; /* MIIF_FDT_xxx boolean properties */ +}; +typedef struct mii_fdt_phy_config mii_fdt_phy_config_t; + +/* PHY config flags. */ +#define MIIF_FDT_COMPAT_CLAUSE45 0x0001 +#define MIIF_FDT_BROKEN_TURNAROUND 0x0002 +#define MIIF_FDT_LANE_SWAP 0x0004 +#define MIIF_FDT_NO_LANE_SWAP 0x0008 +#define MIIF_FDT_EEE_BROKEN_100TX 0x0010 +#define MIIF_FDT_EEE_BROKEN_1000T 0x0020 +#define MIIF_FDT_EEE_BROKEN_10GT 0x0040 +#define MIIF_FDT_EEE_BROKEN_1000KX 0x0080 +#define MIIF_FDT_EEE_BROKEN_10GKX4 0x0100 +#define MIIF_FDT_EEE_BROKEN_10GKR 0x0200 + +/* + * Convert between mii_contype enums and devicetree property strings. + */ +const char *mii_fdt_contype_to_name(mii_contype_t contype); +mii_contype_t mii_fdt_contype_from_name(const char *name); + +/* Get the connection type from the given MAC node. */ +mii_contype_t mii_fdt_get_contype(phandle_t macnode); + +/* + * Get/free the config for the given PHY device. + */ +void mii_fdt_free_config(struct mii_fdt_phy_config *cfg); +mii_fdt_phy_config_t *mii_fdt_get_config(device_t phydev); + +#endif diff --git a/freebsd/sys/dev/mii/miivar.h b/freebsd/sys/dev/mii/miivar.h index 498e7204..ef81bdb2 100644 --- a/freebsd/sys/dev/mii/miivar.h +++ b/freebsd/sys/dev/mii/miivar.h @@ -156,6 +156,42 @@ typedef struct mii_softc mii_softc_t; #define MII_PHY_ANY -1 /* + * Constants used to describe the type of attachment between MAC and PHY. + */ +enum mii_contype { + MII_CONTYPE_UNKNOWN, /* Must be have value 0. */ + + MII_CONTYPE_MII, + MII_CONTYPE_GMII, + MII_CONTYPE_SGMII, + MII_CONTYPE_QSGMII, + MII_CONTYPE_TBI, + MII_CONTYPE_REVMII, /* Reverse MII */ + MII_CONTYPE_RMII, + MII_CONTYPE_RGMII, /* Delays provided by MAC or PCB */ + MII_CONTYPE_RGMII_ID, /* Rx and tx delays provided by PHY */ + MII_CONTYPE_RGMII_RXID, /* Only rx delay provided by PHY */ + MII_CONTYPE_RGMII_TXID, /* Only tx delay provided by PHY */ + MII_CONTYPE_RTBI, + MII_CONTYPE_SMII, + MII_CONTYPE_XGMII, + MII_CONTYPE_TRGMII, + MII_CONTYPE_2000BX, + MII_CONTYPE_2500BX, + MII_CONTYPE_RXAUI, + + MII_CONTYPE_COUNT /* Add new types before this line. */ +}; +typedef enum mii_contype mii_contype_t; + +static inline bool +mii_contype_is_rgmii(mii_contype_t con) +{ + + return (con >= MII_CONTYPE_RGMII && con <= MII_CONTYPE_RGMII_TXID); +} + +/* * Used to attach a PHY to a parent. */ struct mii_attach_args { diff --git a/freebsd/sys/dev/mmc/bridge.h b/freebsd/sys/dev/mmc/bridge.h index a780ffae..53e61b48 100644 --- a/freebsd/sys/dev/mmc/bridge.h +++ b/freebsd/sys/dev/mmc/bridge.h @@ -137,6 +137,10 @@ enum mmc_card_mode { mode_mmc, mode_sd }; +enum mmc_retune_req { + retune_req_none = 0, retune_req_normal, retune_req_reset +}; + struct mmc_host { int f_min; int f_max; @@ -174,15 +178,17 @@ struct mmc_host { struct mmc_ios ios; /* Current state of the host */ }; +#ifdef _KERNEL extern driver_t mmc_driver; extern devclass_t mmc_devclass; -#define MMC_VERSION 3 +#define MMC_VERSION 4 #define MMC_DECLARE_BRIDGE(name) \ DRIVER_MODULE(mmc, name, mmc_driver, mmc_devclass, NULL, NULL); \ MODULE_DEPEND(name, mmc, MMC_VERSION, MMC_VERSION, MMC_VERSION); #define MMC_DEPEND(name) \ MODULE_DEPEND(name, mmc, MMC_VERSION, MMC_VERSION, MMC_VERSION); +#endif /* _KERNEL */ #endif /* DEV_MMC_BRIDGE_H */ diff --git a/freebsd/sys/dev/mmc/mmc.c b/freebsd/sys/dev/mmc/mmc.c index 023091eb..74e26332 100644 --- a/freebsd/sys/dev/mmc/mmc.c +++ b/freebsd/sys/dev/mmc/mmc.c @@ -90,14 +90,14 @@ struct mmc_ivars { uint8_t raw_ext_csd[MMC_EXTCSD_SIZE]; /* Raw bits of the EXT_CSD */ uint32_t raw_sd_status[16]; /* Raw bits of the SD_STATUS */ uint16_t rca; + u_char read_only; /* True when the device is read-only */ + u_char high_cap; /* High Capacity device (block addressed) */ enum mmc_card_mode mode; + enum mmc_bus_width bus_width; /* Bus width to use */ struct mmc_cid cid; /* cid decoded */ struct mmc_csd csd; /* csd decoded */ struct mmc_scr scr; /* scr decoded */ struct mmc_sd_status sd_status; /* SD_STATUS decoded */ - u_char read_only; /* True when the device is read-only */ - u_char bus_width; /* Bus width to use */ - u_char high_cap; /* High Capacity card (block addressed) */ uint32_t sec_count; /* Card capacity in 512byte blocks */ uint32_t timings; /* Mask of bus timings supported */ uint32_t vccq_120; /* Mask of bus timings at VCCQ of 1.2 V */ @@ -129,8 +129,10 @@ static int mmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result); static int mmc_release_bus(device_t busdev, device_t dev); static int mmc_resume(device_t dev); +static void mmc_retune_pause(device_t busdev, device_t dev, bool retune); +static void mmc_retune_unpause(device_t busdev, device_t dev); static int mmc_suspend(device_t dev); -static int mmc_wait_for_request(device_t brdev, device_t reqdev, +static int mmc_wait_for_request(device_t busdev, device_t dev, struct mmc_request *req); static int mmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value); @@ -157,21 +159,23 @@ static void mmc_decode_cid_mmc(uint32_t *raw_cid, struct mmc_cid *cid, bool is_4_41p); static void mmc_decode_cid_sd(uint32_t *raw_cid, struct mmc_cid *cid); static void mmc_decode_csd_mmc(uint32_t *raw_csd, struct mmc_csd *csd); -static void mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd); +static int mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd); static void mmc_delayed_attach(void *xsc); -static int mmc_delete_cards(struct mmc_softc *sc); +static int mmc_delete_cards(struct mmc_softc *sc, bool final); static void mmc_discover_cards(struct mmc_softc *sc); static void mmc_format_card_id_string(struct mmc_ivars *ivar); static void mmc_go_discovery(struct mmc_softc *sc); static uint32_t mmc_get_bits(uint32_t *bits, int bit_len, int start, int size); static int mmc_highest_voltage(uint32_t ocr); +static bool mmc_host_timing(device_t dev, enum mmc_bus_timing timing); static void mmc_idle_cards(struct mmc_softc *sc); static void mmc_ms_delay(int ms); static void mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard); static void mmc_power_down(struct mmc_softc *sc); static void mmc_power_up(struct mmc_softc *sc); static void mmc_rescan_cards(struct mmc_softc *sc); +static int mmc_retune(device_t busdev, device_t dev, bool reset); static void mmc_scan(struct mmc_softc *sc); static int mmc_sd_switch(struct mmc_softc *sc, uint8_t mode, uint8_t grp, uint8_t value, uint8_t *res); @@ -185,15 +189,23 @@ static int mmc_send_op_cond(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr); static int mmc_send_relative_addr(struct mmc_softc *sc, uint32_t *resp); static int mmc_set_blocklen(struct mmc_softc *sc, uint32_t len); -static int mmc_set_card_bus_width(struct mmc_softc *sc, struct mmc_ivars *ivar); +static int mmc_set_card_bus_width(struct mmc_softc *sc, struct mmc_ivars *ivar, + enum mmc_bus_timing timing); static int mmc_set_power_class(struct mmc_softc *sc, struct mmc_ivars *ivar); static int mmc_set_relative_addr(struct mmc_softc *sc, uint16_t resp); static int mmc_set_timing(struct mmc_softc *sc, struct mmc_ivars *ivar, enum mmc_bus_timing timing); +static int mmc_set_vccq(struct mmc_softc *sc, struct mmc_ivars *ivar, + enum mmc_bus_timing timing); +static int mmc_switch_to_hs200(struct mmc_softc *sc, struct mmc_ivars *ivar, + uint32_t clock); +static int mmc_switch_to_hs400(struct mmc_softc *sc, struct mmc_ivars *ivar, + uint32_t max_dtr, enum mmc_bus_timing max_timing); static int mmc_test_bus_width(struct mmc_softc *sc); static uint32_t mmc_timing_to_dtr(struct mmc_ivars *ivar, enum mmc_bus_timing timing); static const char *mmc_timing_to_string(enum mmc_bus_timing timing); +static void mmc_update_child_list(struct mmc_softc *sc); static int mmc_wait_for_command(struct mmc_softc *sc, uint32_t opcode, uint32_t arg, uint32_t flags, uint32_t *resp, int retries); static int mmc_wait_for_req(struct mmc_softc *sc, struct mmc_request *req); @@ -237,7 +249,8 @@ mmc_detach(device_t dev) struct mmc_softc *sc = device_get_softc(dev); int err; - if ((err = mmc_delete_cards(sc)) != 0) + err = mmc_delete_cards(sc, true); + if (err != 0) return (err); mmc_power_down(sc); MMC_LOCK_DESTROY(sc); @@ -252,10 +265,21 @@ mmc_suspend(device_t dev) int err; err = bus_generic_suspend(dev); - if (err) + if (err != 0) + return (err); + /* + * We power down with the bus acquired here, mainly so that no device + * is selected any longer and sc->last_rca gets set to 0. Otherwise, + * the deselect as part of the bus acquisition in mmc_scan() may fail + * during resume, as the bus isn't powered up again before later in + * mmc_go_discovery(). + */ + err = mmc_acquire_bus(dev, dev); + if (err != 0) return (err); mmc_power_down(sc); - return (0); + err = mmc_release_bus(dev, dev); + return (err); } static int @@ -272,7 +296,8 @@ mmc_acquire_bus(device_t busdev, device_t dev) { struct mmc_softc *sc; struct mmc_ivars *ivar; - int err, rca; + int err; + uint16_t rca; enum mmc_bus_timing timing; err = MMCBR_ACQUIRE_HOST(device_get_parent(busdev), busdev); @@ -296,12 +321,27 @@ mmc_acquire_bus(device_t busdev, device_t dev) rca = ivar->rca; if (sc->last_rca != rca) { if (mmc_select_card(sc, rca) != MMC_ERR_NONE) { - device_printf(sc->dev, "Card at relative " - "address %d failed to select.\n", rca); + device_printf(busdev, "Card at relative " + "address %d failed to select\n", rca); return (ENXIO); } sc->last_rca = rca; timing = mmcbr_get_timing(busdev); + /* + * For eMMC modes, setting/updating bus width and VCCQ + * only really is necessary if there actually is more + * than one device on the bus as generally that already + * had to be done by mmc_calculate_clock() or one of + * its calees. Moreover, setting the bus width anew + * can trigger re-tuning (via a CRC error on the next + * CMD), even if not switching between devices an the + * previously selected one is still tuned. Obviously, + * we need to re-tune the host controller if devices + * are actually switched, though. + */ + if (timing >= bus_timing_mmc_ddr52 && + sc->child_count == 1) + return (0); /* Prepare bus width for the new card. */ if (bootverbose || mmc_debug) { device_printf(busdev, @@ -310,38 +350,34 @@ mmc_acquire_bus(device_t busdev, device_t dev) (ivar->bus_width == bus_width_8) ? 8 : 1, mmc_timing_to_string(timing)); } - if (mmc_set_card_bus_width(sc, ivar) != MMC_ERR_NONE) { - device_printf(sc->dev, "Card at relative " - "address %d failed to set bus width.\n", + if (mmc_set_card_bus_width(sc, ivar, timing) != + MMC_ERR_NONE) { + device_printf(busdev, "Card at relative " + "address %d failed to set bus width\n", rca); return (ENXIO); } - if (isset(&ivar->vccq_120, timing)) - mmcbr_set_vccq(busdev, vccq_120); - else if (isset(&ivar->vccq_180, timing)) - mmcbr_set_vccq(busdev, vccq_180); - else - mmcbr_set_vccq(busdev, vccq_330); - if (mmcbr_switch_vccq(busdev) != 0) { - device_printf(sc->dev, "Failed to set VCCQ " - "for card at relative address %d.\n", rca); + mmcbr_set_bus_width(busdev, ivar->bus_width); + mmcbr_update_ios(busdev); + if (mmc_set_vccq(sc, ivar, timing) != MMC_ERR_NONE) { + device_printf(busdev, "Failed to set VCCQ " + "for card at relative address %d\n", rca); return (ENXIO); } - if (mmc_set_power_class(sc, ivar) != MMC_ERR_NONE) { - device_printf(sc->dev, "Card at relative " - "address %d failed to set power class.\n", - rca); + if (timing >= bus_timing_mmc_hs200 && + mmc_retune(busdev, dev, true) != 0) { + device_printf(busdev, "Card at relative " + "address %d failed to re-tune\n", rca); return (ENXIO); } - mmcbr_set_bus_width(busdev, ivar->bus_width); - mmcbr_update_ios(busdev); } } else { /* * If there's a card selected, stand down. */ if (sc->last_rca != 0) { - mmc_select_card(sc, 0); + if (mmc_select_card(sc, 0) != MMC_ERR_NONE) + return (ENXIO); sc->last_rca = 0; } } @@ -416,7 +452,7 @@ mmc_wait_for_req(struct mmc_softc *sc, struct mmc_request *req) #endif /* __rtems__ */ req->done = mmc_wakeup; req->done_data = sc; - if (mmc_debug > 1) { + if (__predict_false(mmc_debug > 1)) { device_printf(sc->dev, "REQUEST: CMD%d arg %#x flags %#x", req->cmd->opcode, req->cmd->arg, req->cmd->flags); if (req->cmd->data) { @@ -434,18 +470,66 @@ mmc_wait_for_req(struct mmc_softc *sc, struct mmc_request *req) rtems_binary_semaphore_wait(&req->req_done); rtems_binary_semaphore_destroy(&req->req_done); #endif /* __rtems__ */ - if (mmc_debug > 2 || (mmc_debug > 0 && req->cmd->error != MMC_ERR_NONE)) + if (__predict_false(mmc_debug > 2 || (mmc_debug > 0 && + req->cmd->error != MMC_ERR_NONE))) device_printf(sc->dev, "CMD%d RESULT: %d\n", req->cmd->opcode, req->cmd->error); return (0); } static int -mmc_wait_for_request(device_t brdev, device_t reqdev __unused, - struct mmc_request *req) +mmc_wait_for_request(device_t busdev, device_t dev, struct mmc_request *req) { - struct mmc_softc *sc = device_get_softc(brdev); + struct mmc_softc *sc; + struct mmc_ivars *ivar; + int err, i; + enum mmc_retune_req retune_req; + + sc = device_get_softc(busdev); + KASSERT(sc->owner != NULL, + ("%s: Request from %s without bus being acquired.", __func__, + device_get_nameunit(dev))); + /* + * Unless no device is selected or re-tuning is already ongoing, + * execute re-tuning if a) the bridge is requesting to do so and + * re-tuning hasn't been otherwise paused, or b) if a child asked + * to be re-tuned prior to pausing (see also mmc_retune_pause()). + */ + if (__predict_false(sc->last_rca != 0 && sc->retune_ongoing == 0 && + (((retune_req = mmcbr_get_retune_req(busdev)) != retune_req_none && + sc->retune_paused == 0) || sc->retune_needed == 1))) { + if (__predict_false(mmc_debug > 1)) { + device_printf(busdev, + "Re-tuning with%s circuit reset required\n", + retune_req == retune_req_reset ? "" : "out"); + } + if (device_get_parent(dev) == busdev) + ivar = device_get_ivars(dev); + else { + for (i = 0; i < sc->child_count; i++) { + ivar = device_get_ivars(sc->child_list[i]); + if (ivar->rca == sc->last_rca) + break; + } + if (ivar->rca != sc->last_rca) + return (EINVAL); + } + sc->retune_ongoing = 1; + err = mmc_retune(busdev, dev, retune_req == retune_req_reset); + sc->retune_ongoing = 0; + switch (err) { + case MMC_ERR_NONE: + case MMC_ERR_FAILED: /* Re-tune error but still might work */ + break; + case MMC_ERR_BADCRC: /* Switch failure on HS400 recovery */ + return (ENXIO); + case MMC_ERR_INVALID: /* Driver implementation b0rken */ + default: /* Unknown error, should not happen */ + return (EINVAL); + } + sc->retune_needed = 0; + } return (mmc_wait_for_req(sc, req)); } @@ -613,11 +697,14 @@ mmc_power_down(struct mmc_softc *sc) static int mmc_select_card(struct mmc_softc *sc, uint16_t rca) { - int flags; + int err, flags; flags = (rca ? MMC_RSP_R1B : MMC_RSP_NONE) | MMC_CMD_AC; - return (mmc_wait_for_command(sc, MMC_SELECT_CARD, (uint32_t)rca << 16, - flags, NULL, CMD_RETRIES)); + sc->retune_paused++; + err = mmc_wait_for_command(sc, MMC_SELECT_CARD, (uint32_t)rca << 16, + flags, NULL, CMD_RETRIES); + sc->retune_paused--; + return (err); } static int @@ -649,7 +736,8 @@ mmc_sd_switch(struct mmc_softc *sc, uint8_t mode, uint8_t grp, uint8_t value, } static int -mmc_set_card_bus_width(struct mmc_softc *sc, struct mmc_ivars *ivar) +mmc_set_card_bus_width(struct mmc_softc *sc, struct mmc_ivars *ivar, + enum mmc_bus_timing timing) { struct mmc_command cmd; int err; @@ -682,28 +770,33 @@ mmc_set_card_bus_width(struct mmc_softc *sc, struct mmc_ivars *ivar) } else { switch (ivar->bus_width) { case bus_width_1: + if (timing == bus_timing_mmc_hs400 || + timing == bus_timing_mmc_hs400es) + return (MMC_ERR_INVALID); value = EXT_CSD_BUS_WIDTH_1; break; case bus_width_4: - switch (mmcbr_get_timing(sc->dev)) { + switch (timing) { case bus_timing_mmc_ddr52: - case bus_timing_mmc_hs200: - case bus_timing_mmc_hs400: - case bus_timing_mmc_hs400es: value = EXT_CSD_BUS_WIDTH_4_DDR; break; + case bus_timing_mmc_hs400: + case bus_timing_mmc_hs400es: + return (MMC_ERR_INVALID); default: value = EXT_CSD_BUS_WIDTH_4; break; } break; case bus_width_8: - switch (mmcbr_get_timing(sc->dev)) { + value = 0; + switch (timing) { + case bus_timing_mmc_hs400es: + value = EXT_CSD_BUS_WIDTH_ES; + /* FALLTHROUGH */ case bus_timing_mmc_ddr52: - case bus_timing_mmc_hs200: case bus_timing_mmc_hs400: - case bus_timing_mmc_hs400es: - value = EXT_CSD_BUS_WIDTH_8_DDR; + value |= EXT_CSD_BUS_WIDTH_8_DDR; break; default: value = EXT_CSD_BUS_WIDTH_8; @@ -828,6 +921,13 @@ mmc_set_timing(struct mmc_softc *sc, struct mmc_ivars *ivar, case bus_timing_mmc_ddr52: value = EXT_CSD_HS_TIMING_HS; break; + case bus_timing_mmc_hs200: + value = EXT_CSD_HS_TIMING_HS200; + break; + case bus_timing_mmc_hs400: + case bus_timing_mmc_hs400es: + value = EXT_CSD_HS_TIMING_HS400; + break; default: return (MMC_ERR_INVALID); } @@ -844,6 +944,23 @@ mmc_set_timing(struct mmc_softc *sc, struct mmc_ivars *ivar, return (err); } +static int +mmc_set_vccq(struct mmc_softc *sc, struct mmc_ivars *ivar, + enum mmc_bus_timing timing) +{ + + if (isset(&ivar->vccq_120, timing)) + mmcbr_set_vccq(sc->dev, vccq_120); + else if (isset(&ivar->vccq_180, timing)) + mmcbr_set_vccq(sc->dev, vccq_180); + else + mmcbr_set_vccq(sc->dev, vccq_330); + if (mmcbr_switch_vccq(sc->dev) != 0) + return (MMC_ERR_INVALID); + else + return (MMC_ERR_NONE); +} + static const uint8_t p8[8] = { 0x55, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; @@ -1051,7 +1168,7 @@ static const int cur_max[8] = { 1000, 5000, 10000, 25000, 35000, 45000, 800000, 200000 }; -static void +static int mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd) { int v; @@ -1092,6 +1209,7 @@ mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd) csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3); csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4); csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1); + return (MMC_ERR_NONE); } else if (v == 1) { m = mmc_get_bits(raw_csd, 128, 115, 4); e = mmc_get_bits(raw_csd, 128, 112, 3); @@ -1115,8 +1233,9 @@ mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd) csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3); csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4); csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1); - } else - panic("unknown SD CSD version"); + return (MMC_ERR_NONE); + } + return (MMC_ERR_INVALID); } static void @@ -1380,6 +1499,53 @@ mmc_timing_to_string(enum mmc_bus_timing timing) return (""); } +static bool +mmc_host_timing(device_t dev, enum mmc_bus_timing timing) +{ + int host_caps; + + host_caps = mmcbr_get_caps(dev); + +#define HOST_TIMING_CAP(host_caps, cap) ({ \ + bool retval; \ + if (((host_caps) & (cap)) == (cap)) \ + retval = true; \ + else \ + retval = false; \ + retval; \ +}) + + switch (timing) { + case bus_timing_normal: + return (true); + case bus_timing_hs: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_HSPEED)); + case bus_timing_uhs_sdr12: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_UHS_SDR12)); + case bus_timing_uhs_sdr25: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_UHS_SDR25)); + case bus_timing_uhs_ddr50: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_UHS_DDR50)); + case bus_timing_uhs_sdr50: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_UHS_SDR50)); + case bus_timing_uhs_sdr104: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_UHS_SDR104)); + case bus_timing_mmc_ddr52: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_DDR52)); + case bus_timing_mmc_hs200: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS200)); + case bus_timing_mmc_hs400: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS400)); + case bus_timing_mmc_hs400es: + return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS400 | + MMC_CAP_MMC_ENH_STROBE)); + } + +#undef HOST_TIMING_CAP + + return (false); +} + static void mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard) { @@ -1411,9 +1577,8 @@ mmc_discover_cards(struct mmc_softc *sc) u_char switch_res[64]; uint32_t raw_cid[4]; struct mmc_ivars *ivar = NULL; - device_t *devlist; device_t child; - int devcount, err, host_caps, i, newcard; + int err, host_caps, i, newcard; uint32_t resp, sec_count, status; uint16_t rca = 2; @@ -1421,6 +1586,7 @@ mmc_discover_cards(struct mmc_softc *sc) if (bootverbose || mmc_debug) device_printf(sc->dev, "Probing cards\n"); while (1) { + child = NULL; sc->squelched++; /* Errors are expected, squelch reporting. */ err = mmc_all_send_cid(sc, raw_cid); sc->squelched--; @@ -1431,18 +1597,14 @@ mmc_discover_cards(struct mmc_softc *sc) break; } newcard = 1; - if ((err = device_get_children(sc->dev, &devlist, - &devcount)) != 0) - return; - for (i = 0; i < devcount; i++) { - ivar = device_get_ivars(devlist[i]); + for (i = 0; i < sc->child_count; i++) { + ivar = device_get_ivars(sc->child_list[i]); if (memcmp(ivar->raw_cid, raw_cid, sizeof(raw_cid)) == 0) { newcard = 0; break; } } - free(devlist, M_TEMP); if (bootverbose || mmc_debug) { device_printf(sc->dev, "%sard detected (CID %08x%08x%08x%08x)\n", @@ -1465,7 +1627,7 @@ mmc_discover_cards(struct mmc_softc *sc) if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error getting RCA %d\n", err); - break; + goto free_ivar; } ivar->rca = resp >> 16; /* Get card CSD. */ @@ -1473,7 +1635,7 @@ mmc_discover_cards(struct mmc_softc *sc) if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error getting CSD %d\n", err); - break; + goto free_ivar; } if (bootverbose || mmc_debug) device_printf(sc->dev, @@ -1481,7 +1643,11 @@ mmc_discover_cards(struct mmc_softc *sc) newcard ? "New c" : "C", ivar->raw_csd[0], ivar->raw_csd[1], ivar->raw_csd[2], ivar->raw_csd[3]); - mmc_decode_csd_sd(ivar->raw_csd, &ivar->csd); + err = mmc_decode_csd_sd(ivar->raw_csd, &ivar->csd); + if (err != MMC_ERR_NONE) { + device_printf(sc->dev, "Error decoding CSD\n"); + goto free_ivar; + } ivar->sec_count = ivar->csd.capacity / MMC_SECTOR_SIZE; if (ivar->csd.csd_structure > 0) ivar->high_cap = 1; @@ -1494,12 +1660,12 @@ mmc_discover_cards(struct mmc_softc *sc) if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error reading card status %d\n", err); - break; + goto free_ivar; } if ((status & R1_CARD_IS_LOCKED) != 0) { device_printf(sc->dev, - "Card is password protected, skipping.\n"); - break; + "Card is password protected, skipping\n"); + goto free_ivar; } /* Get card SCR. Card must be selected to fetch it. */ @@ -1507,13 +1673,13 @@ mmc_discover_cards(struct mmc_softc *sc) if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error selecting card %d\n", err); - break; + goto free_ivar; } err = mmc_app_send_scr(sc, ivar->rca, ivar->raw_scr); if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error reading SCR %d\n", err); - break; + goto free_ivar; } mmc_app_decode_scr(ivar->raw_scr, &ivar->scr); /* Get card switch capabilities (command class 10). */ @@ -1541,7 +1707,7 @@ mmc_discover_cards(struct mmc_softc *sc) * use from the sd_status is the erase sector size, but * it is still nice to get that right. */ - mmc_select_card(sc, 0); + (void)mmc_select_card(sc, 0); (void)mmc_select_card(sc, ivar->rca); (void)mmc_app_sd_status(sc, ivar->rca, ivar->raw_sd_status); @@ -1551,47 +1717,24 @@ mmc_discover_cards(struct mmc_softc *sc) ivar->erase_sector = 16 << ivar->sd_status.au_size; } - /* Find max supported bus width. */ + /* Find maximum supported bus width. */ if ((host_caps & MMC_CAP_4_BIT_DATA) && (ivar->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) ivar->bus_width = bus_width_4; - /* - * Some cards that report maximum I/O block sizes - * greater than 512 require the block length to be - * set to 512, even though that is supposed to be - * the default. Example: - * - * Transcend 2GB SDSC card, CID: - * mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=00.2000 - */ - if (ivar->csd.read_bl_len != MMC_SECTOR_SIZE || - ivar->csd.write_bl_len != MMC_SECTOR_SIZE) - mmc_set_blocklen(sc, MMC_SECTOR_SIZE); - - mmc_format_card_id_string(ivar); - - if (bootverbose || mmc_debug) - mmc_log_card(sc->dev, ivar, newcard); - if (newcard) { - /* Add device. */ - child = device_add_child(sc->dev, NULL, -1); - device_set_ivars(child, ivar); - } - mmc_select_card(sc, 0); - return; + goto child_common; } ivar->rca = rca++; err = mmc_set_relative_addr(sc, ivar->rca); if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error setting RCA %d\n", err); - break; + goto free_ivar; } /* Get card CSD. */ err = mmc_send_csd(sc, ivar->rca, ivar->raw_csd); if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error getting CSD %d\n", err); - break; + goto free_ivar; } if (bootverbose || mmc_debug) device_printf(sc->dev, @@ -1610,19 +1753,19 @@ mmc_discover_cards(struct mmc_softc *sc) if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error reading card status %d\n", err); - break; + goto free_ivar; } if ((status & R1_CARD_IS_LOCKED) != 0) { device_printf(sc->dev, - "Card is password protected, skipping.\n"); - break; + "Card is password protected, skipping\n"); + goto free_ivar; } err = mmc_select_card(sc, ivar->rca); if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error selecting card %d\n", err); - break; + goto free_ivar; } /* Only MMC >= 4.x devices support EXT_CSD. */ @@ -1632,7 +1775,7 @@ mmc_discover_cards(struct mmc_softc *sc) if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error reading EXT_CSD %d\n", err); - break; + goto free_ivar; } /* Handle extended capacity from EXT_CSD */ sec_count = ivar->raw_ext_csd[EXT_CSD_SEC_CNT] + @@ -1643,6 +1786,8 @@ mmc_discover_cards(struct mmc_softc *sc) ivar->sec_count = sec_count; ivar->high_cap = 1; } + /* Find maximum supported bus width. */ + ivar->bus_width = mmc_test_bus_width(sc); /* Get device speeds beyond normal mode. */ if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_HS_52) != 0) { @@ -1665,6 +1810,50 @@ mmc_discover_cards(struct mmc_softc *sc) setbit(&ivar->timings, bus_timing_mmc_ddr52); setbit(&ivar->vccq_180, bus_timing_mmc_ddr52); } + if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & + EXT_CSD_CARD_TYPE_HS200_1_2V) != 0 && + (host_caps & MMC_CAP_SIGNALING_120) != 0) { + setbit(&ivar->timings, bus_timing_mmc_hs200); + setbit(&ivar->vccq_120, bus_timing_mmc_hs200); + } + if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & + EXT_CSD_CARD_TYPE_HS200_1_8V) != 0 && + (host_caps & MMC_CAP_SIGNALING_180) != 0) { + setbit(&ivar->timings, bus_timing_mmc_hs200); + setbit(&ivar->vccq_180, bus_timing_mmc_hs200); + } + if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & + EXT_CSD_CARD_TYPE_HS400_1_2V) != 0 && + (host_caps & MMC_CAP_SIGNALING_120) != 0 && + ivar->bus_width == bus_width_8) { + setbit(&ivar->timings, bus_timing_mmc_hs400); + setbit(&ivar->vccq_120, bus_timing_mmc_hs400); + } + if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & + EXT_CSD_CARD_TYPE_HS400_1_8V) != 0 && + (host_caps & MMC_CAP_SIGNALING_180) != 0 && + ivar->bus_width == bus_width_8) { + setbit(&ivar->timings, bus_timing_mmc_hs400); + setbit(&ivar->vccq_180, bus_timing_mmc_hs400); + } + if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & + EXT_CSD_CARD_TYPE_HS400_1_2V) != 0 && + (ivar->raw_ext_csd[EXT_CSD_STROBE_SUPPORT] & + EXT_CSD_STROBE_SUPPORT_EN) != 0 && + (host_caps & MMC_CAP_SIGNALING_120) != 0 && + ivar->bus_width == bus_width_8) { + setbit(&ivar->timings, bus_timing_mmc_hs400es); + setbit(&ivar->vccq_120, bus_timing_mmc_hs400es); + } + if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & + EXT_CSD_CARD_TYPE_HS400_1_8V) != 0 && + (ivar->raw_ext_csd[EXT_CSD_STROBE_SUPPORT] & + EXT_CSD_STROBE_SUPPORT_EN) != 0 && + (host_caps & MMC_CAP_SIGNALING_180) != 0 && + ivar->bus_width == bus_width_8) { + setbit(&ivar->timings, bus_timing_mmc_hs400es); + setbit(&ivar->vccq_180, bus_timing_mmc_hs400es); + } /* * Determine generic switch timeout (provided in * units of 10 ms), defaulting to 500 ms. @@ -1673,8 +1862,6 @@ mmc_discover_cards(struct mmc_softc *sc) if (ivar->csd.spec_vers >= 6) ivar->cmd6_time = 10 * ivar->raw_ext_csd[EXT_CSD_GEN_CMD6_TIME]; - /* Find max supported bus width. */ - ivar->bus_width = mmc_test_bus_width(sc); /* Handle HC erase sector size. */ if (ivar->raw_ext_csd[EXT_CSD_ERASE_GRP_SIZE] != 0) { ivar->erase_sector = 1024 * @@ -1688,11 +1875,15 @@ mmc_discover_cards(struct mmc_softc *sc) device_printf(sc->dev, "Error setting erase group %d\n", err); - break; + goto free_ivar; } } } + mmc_decode_cid_mmc(ivar->raw_cid, &ivar->cid, + ivar->raw_ext_csd[EXT_CSD_REV] >= 5); + +child_common: /* * Some cards that report maximum I/O block sizes greater * than 512 require the block length to be set to 512, even @@ -1705,8 +1896,6 @@ mmc_discover_cards(struct mmc_softc *sc) ivar->csd.write_bl_len != MMC_SECTOR_SIZE) mmc_set_blocklen(sc, MMC_SECTOR_SIZE); - mmc_decode_cid_mmc(ivar->raw_cid, &ivar->cid, - ivar->raw_ext_csd[EXT_CSD_REV] >= 5); mmc_format_card_id_string(ivar); if (bootverbose || mmc_debug) @@ -1714,56 +1903,111 @@ mmc_discover_cards(struct mmc_softc *sc) if (newcard) { /* Add device. */ child = device_add_child(sc->dev, NULL, -1); - device_set_ivars(child, ivar); + if (child != NULL) { + device_set_ivars(child, ivar); + sc->child_list = realloc(sc->child_list, + sizeof(device_t) * sc->child_count + 1, + M_DEVBUF, M_WAITOK); + sc->child_list[sc->child_count++] = child; + } else + device_printf(sc->dev, "Error adding child\n"); } - mmc_select_card(sc, 0); + +free_ivar: + if (newcard && child == NULL) + free(ivar, M_DEVBUF); + (void)mmc_select_card(sc, 0); + /* + * Not returning here when one MMC device could no be added + * potentially would mean looping forever when that device + * is broken (in which case it also may impact the remainder + * of the bus anyway, though). + */ + if ((newcard && child == NULL) || + mmcbr_get_mode(sc->dev) == mode_sd) + return; } } static void +mmc_update_child_list(struct mmc_softc *sc) +{ + device_t child; + int i, j; + + if (sc->child_count == 0) { + free(sc->child_list, M_DEVBUF); + return; + } + for (i = j = 0; i < sc->child_count; i++) { + for (;;) { + child = sc->child_list[j++]; + if (child != NULL) + break; + } + if (i != j) + sc->child_list[i] = child; + } + sc->child_list = realloc(sc->child_list, sizeof(device_t) * + sc->child_count, M_DEVBUF, M_WAITOK); +} + +static void mmc_rescan_cards(struct mmc_softc *sc) { struct mmc_ivars *ivar; - device_t *devlist; - int err, i, devcount; + int err, i, j; - if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0) - return; - for (i = 0; i < devcount; i++) { - ivar = device_get_ivars(devlist[i]); + for (i = j = 0; i < sc->child_count; i++) { + ivar = device_get_ivars(sc->child_list[i]); if (mmc_select_card(sc, ivar->rca) != MMC_ERR_NONE) { if (bootverbose || mmc_debug) device_printf(sc->dev, - "Card at relative address %d lost.\n", + "Card at relative address %d lost\n", ivar->rca); - device_delete_child(sc->dev, devlist[i]); + err = device_delete_child(sc->dev, sc->child_list[i]); + if (err != 0) { + j++; + continue; + } free(ivar, M_DEVBUF); - } + } else + j++; } - free(devlist, M_TEMP); - mmc_select_card(sc, 0); + if (sc->child_count == j) + goto out; + sc->child_count = j; + mmc_update_child_list(sc); +out: + (void)mmc_select_card(sc, 0); } static int -mmc_delete_cards(struct mmc_softc *sc) +mmc_delete_cards(struct mmc_softc *sc, bool final) { struct mmc_ivars *ivar; - device_t *devlist; - int err, i, devcount; + int err, i, j; - if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0) - return (err); - for (i = 0; i < devcount; i++) { - ivar = device_get_ivars(devlist[i]); + err = 0; + for (i = j = 0; i < sc->child_count; i++) { + ivar = device_get_ivars(sc->child_list[i]); if (bootverbose || mmc_debug) device_printf(sc->dev, - "Card at relative address %d deleted.\n", + "Card at relative address %d deleted\n", ivar->rca); - device_delete_child(sc->dev, devlist[i]); + err = device_delete_child(sc->dev, sc->child_list[i]); + if (err != 0) { + j++; + if (final == false) + continue; + else + break; + } free(ivar, M_DEVBUF); } - free(devlist, M_TEMP); - return (0); + sc->child_count = j; + mmc_update_child_list(sc); + return (err); } static void @@ -1827,7 +2071,7 @@ mmc_go_discovery(struct mmc_softc *sc) mmcbr_get_ocr(dev)); if (mmcbr_get_ocr(dev) == 0) { device_printf(sc->dev, "No compatible cards found on bus\n"); - mmc_delete_cards(sc); + (void)mmc_delete_cards(sc, false); mmc_power_down(sc); return; } @@ -1851,31 +2095,27 @@ mmc_go_discovery(struct mmc_softc *sc) static int mmc_calculate_clock(struct mmc_softc *sc) { - device_t *kids; + device_t dev; struct mmc_ivars *ivar; - int host_caps, i, nkid; + int i; uint32_t dtr, max_dtr; + uint16_t rca; enum mmc_bus_timing max_timing, timing; - bool changed; + bool changed, hs400; - max_dtr = mmcbr_get_f_max(sc->dev); - host_caps = mmcbr_get_caps(sc->dev); - if ((host_caps & MMC_CAP_MMC_DDR52) != 0) - max_timing = bus_timing_mmc_ddr52; - else if ((host_caps & MMC_CAP_HSPEED) != 0) - max_timing = bus_timing_hs; - else - max_timing = bus_timing_normal; - if (device_get_children(sc->dev, &kids, &nkid) != 0) - panic("can't get children"); + dev = sc->dev; + max_dtr = mmcbr_get_f_max(dev); + max_timing = bus_timing_max; do { changed = false; - for (i = 0; i < nkid; i++) { - ivar = device_get_ivars(kids[i]); - if (isclr(&ivar->timings, max_timing)) { - for (timing = max_timing; timing >= + for (i = 0; i < sc->child_count; i++) { + ivar = device_get_ivars(sc->child_list[i]); + if (isclr(&ivar->timings, max_timing) || + !mmc_host_timing(dev, max_timing)) { + for (timing = max_timing - 1; timing >= bus_timing_normal; timing--) { - if (isset(&ivar->timings, timing)) { + if (isset(&ivar->timings, timing) && + mmc_host_timing(dev, timing)) { max_timing = timing; break; } @@ -1889,38 +2129,316 @@ mmc_calculate_clock(struct mmc_softc *sc) } } } while (changed == true); + if (bootverbose || mmc_debug) { - device_printf(sc->dev, + device_printf(dev, "setting transfer rate to %d.%03dMHz (%s timing)\n", max_dtr / 1000000, (max_dtr / 1000) % 1000, mmc_timing_to_string(max_timing)); } - for (i = 0; i < nkid; i++) { - ivar = device_get_ivars(kids[i]); + + /* + * HS400 must be tuned in HS200 mode, so in case of HS400 we begin + * with HS200 following the sequence as described in "6.6.2.2 HS200 + * timing mode selection" of the eMMC specification v5.1, too, and + * switch to max_timing later. HS400ES requires no tuning and, thus, + * can be switch to directly, but requires the same detour via high + * speed mode as does HS400 (see mmc_switch_to_hs400()). + */ + hs400 = max_timing == bus_timing_mmc_hs400; + timing = hs400 == true ? bus_timing_mmc_hs200 : max_timing; + for (i = 0; i < sc->child_count; i++) { + ivar = device_get_ivars(sc->child_list[i]); if ((ivar->timings & ~(1 << bus_timing_normal)) == 0) continue; - if (mmc_select_card(sc, ivar->rca) != MMC_ERR_NONE || - mmc_set_timing(sc, ivar, max_timing) != MMC_ERR_NONE) - device_printf(sc->dev, "Card at relative address %d " - "failed to set timing.\n", ivar->rca); + + rca = ivar->rca; + if (mmc_select_card(sc, rca) != MMC_ERR_NONE) { + device_printf(dev, "Card at relative address %d " + "failed to select\n", rca); + continue; + } + + if (timing == bus_timing_mmc_hs200 || /* includes HS400 */ + timing == bus_timing_mmc_hs400es) { + if (mmc_set_vccq(sc, ivar, timing) != MMC_ERR_NONE) { + device_printf(dev, "Failed to set VCCQ for " + "card at relative address %d\n", rca); + continue; + } + } + + if (timing == bus_timing_mmc_hs200) { /* includes HS400 */ + /* Set bus width (required for initial tuning). */ + if (mmc_set_card_bus_width(sc, ivar, timing) != + MMC_ERR_NONE) { + device_printf(dev, "Card at relative address " + "%d failed to set bus width\n", rca); + continue; + } + mmcbr_set_bus_width(dev, ivar->bus_width); + mmcbr_update_ios(dev); + } else if (timing == bus_timing_mmc_hs400es) { + if (mmc_switch_to_hs400(sc, ivar, max_dtr, timing) != + MMC_ERR_NONE) { + device_printf(dev, "Card at relative address " + "%d failed to set %s timing\n", rca, + mmc_timing_to_string(timing)); + continue; + } + goto power_class; + } + + if (mmc_set_timing(sc, ivar, timing) != MMC_ERR_NONE) { + device_printf(dev, "Card at relative address %d " + "failed to set %s timing\n", rca, + mmc_timing_to_string(timing)); + continue; + } + + if (timing == bus_timing_mmc_ddr52) { + /* + * Set EXT_CSD_BUS_WIDTH_n_DDR in EXT_CSD_BUS_WIDTH + * (must be done after switching to EXT_CSD_HS_TIMING). + */ + if (mmc_set_card_bus_width(sc, ivar, timing) != + MMC_ERR_NONE) { + device_printf(dev, "Card at relative address " + "%d failed to set bus width\n", rca); + continue; + } + mmcbr_set_bus_width(dev, ivar->bus_width); + mmcbr_update_ios(dev); + if (mmc_set_vccq(sc, ivar, timing) != MMC_ERR_NONE) { + device_printf(dev, "Failed to set VCCQ for " + "card at relative address %d\n", rca); + continue; + } + } + + /* Set clock (must be done before initial tuning). */ + mmcbr_set_clock(dev, max_dtr); + mmcbr_update_ios(dev); + + if (mmcbr_tune(dev, hs400) != 0) { + device_printf(dev, "Card at relative address %d " + "failed to execute initial tuning\n", rca); + continue; + } + + if (hs400 == true && mmc_switch_to_hs400(sc, ivar, max_dtr, + max_timing) != MMC_ERR_NONE) { + device_printf(dev, "Card at relative address %d " + "failed to set %s timing\n", rca, + mmc_timing_to_string(max_timing)); + continue; + } + +power_class: + if (mmc_set_power_class(sc, ivar) != MMC_ERR_NONE) { + device_printf(dev, "Card at relative address %d " + "failed to set power class\n", rca); + } } - mmc_select_card(sc, 0); - free(kids, M_TEMP); - mmcbr_set_clock(sc->dev, max_dtr); - mmcbr_update_ios(sc->dev); + (void)mmc_select_card(sc, 0); return (max_dtr); } +/* + * Switch from HS200 to HS400 (either initially or for re-tuning) or directly + * to HS400ES. This follows the sequences described in "6.6.2.3 HS400 timing + * mode selection" of the eMMC specification v5.1. + */ +static int +mmc_switch_to_hs400(struct mmc_softc *sc, struct mmc_ivars *ivar, + uint32_t clock, enum mmc_bus_timing max_timing) +{ + device_t dev; + int err; + uint16_t rca; + + dev = sc->dev; + rca = ivar->rca; + + /* + * Both clock and timing must be set as appropriate for high speed + * before eventually switching to HS400/HS400ES; mmc_set_timing() + * will issue mmcbr_update_ios(). + */ + mmcbr_set_clock(dev, ivar->hs_tran_speed); + err = mmc_set_timing(sc, ivar, bus_timing_hs); + if (err != MMC_ERR_NONE) + return (err); + + /* + * Set EXT_CSD_BUS_WIDTH_8_DDR in EXT_CSD_BUS_WIDTH (and additionally + * EXT_CSD_BUS_WIDTH_ES for HS400ES). + */ + err = mmc_set_card_bus_width(sc, ivar, max_timing); + if (err != MMC_ERR_NONE) + return (err); + mmcbr_set_bus_width(dev, ivar->bus_width); + mmcbr_update_ios(dev); + + /* Finally, switch to HS400/HS400ES mode. */ + err = mmc_set_timing(sc, ivar, max_timing); + if (err != MMC_ERR_NONE) + return (err); + mmcbr_set_clock(dev, clock); + mmcbr_update_ios(dev); + return (MMC_ERR_NONE); +} + +/* + * Switch from HS400 to HS200 (for re-tuning). + */ +static int +mmc_switch_to_hs200(struct mmc_softc *sc, struct mmc_ivars *ivar, + uint32_t clock) +{ + device_t dev; + int err; + uint16_t rca; + + dev = sc->dev; + rca = ivar->rca; + + /* + * Both clock and timing must initially be set as appropriate for + * DDR52 before eventually switching to HS200; mmc_set_timing() + * will issue mmcbr_update_ios(). + */ + mmcbr_set_clock(dev, ivar->hs_tran_speed); + err = mmc_set_timing(sc, ivar, bus_timing_mmc_ddr52); + if (err != MMC_ERR_NONE) + return (err); + + /* + * Next, switch to high speed. Thus, clear EXT_CSD_BUS_WIDTH_n_DDR + * in EXT_CSD_BUS_WIDTH and update bus width and timing in ios. + */ + err = mmc_set_card_bus_width(sc, ivar, bus_timing_hs); + if (err != MMC_ERR_NONE) + return (err); + mmcbr_set_bus_width(dev, ivar->bus_width); + mmcbr_set_timing(sc->dev, bus_timing_hs); + mmcbr_update_ios(dev); + + /* Finally, switch to HS200 mode. */ + err = mmc_set_timing(sc, ivar, bus_timing_mmc_hs200); + if (err != MMC_ERR_NONE) + return (err); + mmcbr_set_clock(dev, clock); + mmcbr_update_ios(dev); + return (MMC_ERR_NONE); +} + +static int +mmc_retune(device_t busdev, device_t dev, bool reset) +{ + struct mmc_softc *sc; + struct mmc_ivars *ivar; + int err; + uint32_t clock; + enum mmc_bus_timing timing; + + if (device_get_parent(dev) != busdev) + return (MMC_ERR_INVALID); + + sc = device_get_softc(busdev); + if (sc->retune_needed != 1 && sc->retune_paused != 0) + return (MMC_ERR_INVALID); + + timing = mmcbr_get_timing(busdev); + if (timing == bus_timing_mmc_hs400) { + /* + * Controllers use the data strobe line to latch data from + * the devices in HS400 mode so periodic re-tuning isn't + * expected to be required, i. e. only if a CRC or tuning + * error is signaled to the bridge. In these latter cases + * we are asked to reset the tuning circuit and need to do + * the switch timing dance. + */ + if (reset == false) + return (0); + ivar = device_get_ivars(dev); + clock = mmcbr_get_clock(busdev); + if (mmc_switch_to_hs200(sc, ivar, clock) != MMC_ERR_NONE) + return (MMC_ERR_BADCRC); + } + err = mmcbr_retune(busdev, reset); + if (err != 0 && timing == bus_timing_mmc_hs400) + return (MMC_ERR_BADCRC); + switch (err) { + case 0: + break; + case EIO: + return (MMC_ERR_FAILED); + default: + return (MMC_ERR_INVALID); + } + if (timing == bus_timing_mmc_hs400) { + if (mmc_switch_to_hs400(sc, ivar, clock, timing) != + MMC_ERR_NONE) + return (MMC_ERR_BADCRC); + } + return (MMC_ERR_NONE); +} + +static void +mmc_retune_pause(device_t busdev, device_t dev, bool retune) +{ + struct mmc_softc *sc; + + sc = device_get_softc(busdev); + KASSERT(device_get_parent(dev) == busdev, + ("%s: %s is not a child of %s", __func__, device_get_nameunit(dev), + device_get_nameunit(busdev))); + KASSERT(sc->owner != NULL, + ("%s: Request from %s without bus being acquired.", __func__, + device_get_nameunit(dev))); + + if (retune == true && sc->retune_paused == 0) + sc->retune_needed = 1; + sc->retune_paused++; +} + +static void +mmc_retune_unpause(device_t busdev, device_t dev) +{ + struct mmc_softc *sc; + + sc = device_get_softc(busdev); + KASSERT(device_get_parent(dev) == busdev, + ("%s: %s is not a child of %s", __func__, device_get_nameunit(dev), + device_get_nameunit(busdev))); + KASSERT(sc->owner != NULL, + ("%s: Request from %s without bus being acquired.", __func__, + device_get_nameunit(dev))); + KASSERT(sc->retune_paused != 0, + ("%s: Re-tune pause count already at 0", __func__)); + + sc->retune_paused--; +} + static void mmc_scan(struct mmc_softc *sc) { device_t dev = sc->dev; + int err; - mmc_acquire_bus(dev, dev); + err = mmc_acquire_bus(dev, dev); + if (err != 0) { + device_printf(dev, "Failed to acquire bus for scanning\n"); + return; + } mmc_go_discovery(sc); - mmc_release_bus(dev, dev); - - bus_generic_attach(dev); + err = mmc_release_bus(dev, dev); + if (err != 0) { + device_printf(dev, "Failed to release bus after scanning\n"); + return; + } + (void)bus_generic_attach(dev); } static int @@ -2019,6 +2537,8 @@ static device_method_t mmc_methods[] = { DEVMETHOD(bus_child_location_str, mmc_child_location_str), /* MMC Bus interface */ + DEVMETHOD(mmcbus_retune_pause, mmc_retune_pause), + DEVMETHOD(mmcbus_retune_unpause, mmc_retune_unpause), DEVMETHOD(mmcbus_wait_for_request, mmc_wait_for_request), DEVMETHOD(mmcbus_acquire_bus, mmc_acquire_bus), DEVMETHOD(mmcbus_release_bus, mmc_release_bus), diff --git a/freebsd/sys/dev/mmc/mmc_ioctl.h b/freebsd/sys/dev/mmc/mmc_ioctl.h index 97cff068..e633fec9 100644 --- a/freebsd/sys/dev/mmc/mmc_ioctl.h +++ b/freebsd/sys/dev/mmc/mmc_ioctl.h @@ -54,7 +54,7 @@ struct mmc_ioc_multi_cmd { #define MMC_IOC_BASE 'M' #define MMC_IOC_CMD _IOWR(MMC_IOC_BASE, 0, struct mmc_ioc_cmd) -#define MMC_IOC_CMD_MULTI _IOWR(MMC_IOC_BASE, 1, struct mmc_ioc_multi_cmd) +#define MMC_IOC_MULTI_CMD _IOWR(MMC_IOC_BASE, 1, struct mmc_ioc_multi_cmd) /* Maximum accepted data transfer size */ #define MMC_IOC_MAX_BYTES (512 * 256) diff --git a/freebsd/sys/dev/mmc/mmc_private.h b/freebsd/sys/dev/mmc/mmc_private.h index bbca0c60..633d0784 100644 --- a/freebsd/sys/dev/mmc/mmc_private.h +++ b/freebsd/sys/dev/mmc/mmc_private.h @@ -60,9 +60,14 @@ struct mmc_softc { struct mtx sc_mtx; struct intr_config_hook config_intrhook; device_t owner; - uint32_t last_rca; - int squelched; /* suppress reporting of (expected) errors */ - int log_count; + device_t *child_list; + int child_count; + uint16_t last_rca; + uint16_t retune_paused; + uint8_t retune_needed; + uint8_t retune_ongoing; + uint16_t squelched; /* suppress reporting of (expected) errors */ + int log_count; struct timeval log_time; }; diff --git a/freebsd/sys/dev/mmc/mmc_subr.c b/freebsd/sys/dev/mmc/mmc_subr.c index f76e9637..006354ba 100644 --- a/freebsd/sys/dev/mmc/mmc_subr.c +++ b/freebsd/sys/dev/mmc/mmc_subr.c @@ -140,7 +140,6 @@ mmc_wait_for_app_cmd(device_t brdev, device_t reqdev, uint16_t rca, sc->squelched--; if (err != MMC_ERR_NONE && brdev == reqdev) { - sc = device_get_softc(brdev); if (sc->squelched == 0 && ppsratecheck(&sc->log_time, &sc->log_count, LOG_PPS)) { device_printf(sc->dev, "ACMD%d failed, RESULT: %d\n", @@ -156,10 +155,13 @@ mmc_switch(device_t brdev, device_t reqdev, uint16_t rca, uint8_t set, uint8_t index, uint8_t value, u_int timeout, bool status) { struct mmc_command cmd; + struct mmc_softc *sc; int err; KASSERT(timeout != 0, ("%s: no timeout", __func__)); + sc = device_get_softc(brdev); + memset(&cmd, 0, sizeof(cmd)); cmd.opcode = MMC_SWITCH_FUNC; cmd.arg = (MMC_SWITCH_FUNC_WR << 24) | (index << 16) | (value << 8) | @@ -174,10 +176,19 @@ mmc_switch(device_t brdev, device_t reqdev, uint16_t rca, uint8_t set, cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; else cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + /* + * Pause re-tuning so it won't interfere with the busy state and also + * so that the result of CMD13 will always refer to switching rather + * than to a tuning command that may have snuck in between. + */ + sc->retune_paused++; err = mmc_wait_for_cmd(brdev, reqdev, &cmd, CMD_RETRIES); if (err != MMC_ERR_NONE || status == false) - return (err); - return (mmc_switch_status(brdev, reqdev, rca, timeout)); + goto out; + err = mmc_switch_status(brdev, reqdev, rca, timeout); +out: + sc->retune_paused--; + return (err); } int @@ -194,6 +205,7 @@ mmc_switch_status(device_t brdev, device_t reqdev, uint16_t rca, u_int timeout) * type MMC_CAP_WAIT_WHILE_BUSY will issue mmc_send_status() only * once and then exit the loop. */ + end.tv_sec = end.tv_usec = 0; for (;;) { err = mmc_send_status(brdev, reqdev, rca, &status); if (err != MMC_ERR_NONE) @@ -210,7 +222,7 @@ mmc_switch_status(device_t brdev, device_t reqdev, uint16_t rca, u_int timeout) break; } } - if (err == MMC_ERR_NONE && R1_CURRENT_STATE(status) == R1_SWITCH_ERROR) + if (err == MMC_ERR_NONE && (status & R1_SWITCH_ERROR) != 0) return (MMC_ERR_FAILED); return (err); } diff --git a/freebsd/sys/dev/mmc/mmcbrvar.h b/freebsd/sys/dev/mmc/mmcbrvar.h index 77c304b4..c70af92a 100644 --- a/freebsd/sys/dev/mmc/mmcbrvar.h +++ b/freebsd/sys/dev/mmc/mmcbrvar.h @@ -70,6 +70,7 @@ enum mmcbr_device_ivars { MMCBR_IVAR_MODE, MMCBR_IVAR_OCR, MMCBR_IVAR_POWER_MODE, + MMCBR_IVAR_RETUNE_REQ, MMCBR_IVAR_VDD, MMCBR_IVAR_VCCQ, MMCBR_IVAR_CAPS, @@ -94,6 +95,7 @@ MMCBR_ACCESSOR(host_ocr, HOST_OCR, int) MMCBR_ACCESSOR(mode, MODE, int) MMCBR_ACCESSOR(ocr, OCR, int) MMCBR_ACCESSOR(power_mode, POWER_MODE, int) +MMCBR_ACCESSOR(retune_req, RETUNE_REQ, int) MMCBR_ACCESSOR(vdd, VDD, int) MMCBR_ACCESSOR(vccq, VCCQ, int) MMCBR_ACCESSOR(caps, CAPS, int) @@ -109,6 +111,20 @@ mmcbr_update_ios(device_t dev) } static int __inline +mmcbr_tune(device_t dev, bool hs400) +{ + + return (MMCBR_TUNE(device_get_parent(dev), dev, hs400)); +} + +static int __inline +mmcbr_retune(device_t dev, bool reset) +{ + + return (MMCBR_RETUNE(device_get_parent(dev), dev, reset)); +} + +static int __inline mmcbr_switch_vccq(device_t dev) { diff --git a/freebsd/sys/dev/mmc/mmcreg.h b/freebsd/sys/dev/mmc/mmcreg.h index 39680ad6..80f433c1 100644 --- a/freebsd/sys/dev/mmc/mmcreg.h +++ b/freebsd/sys/dev/mmc/mmcreg.h @@ -1,6 +1,7 @@ /*- * Copyright (c) 2006 M. Warner Losh. All rights reserved. * Copyright (c) 2017 Marius Strobl <marius@FreeBSD.org> + * Copyright (c) 2015-2016 Ilya Bakulin <kibab@FreeBSD.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -159,6 +160,34 @@ struct mmc_command { #define R1_STATE_PRG 7 #define R1_STATE_DIS 8 +/* R4 response (SDIO) */ +#define R4_IO_NUM_FUNCTIONS(ocr) (((ocr) >> 28) & 0x3) +#define R4_IO_MEM_PRESENT (0x1<<27) +#define R4_IO_OCR_MASK 0x00fffff0 + +/* + * R5 responses + * + * Types (per SD 2.0 standard) + *e : error bit + *s : status bit + *r : detected and set for the actual command response + *x : Detected and set during command execution. The host can get + * the status by issuing a command with R1 response. + * + * Clear Condition (per SD 2.0 standard) + *a : according to the card current state. + *b : always related to the previous command. reception of a valid + * command will clear it (with a delay of one command). + *c : clear by read + */ +#define R5_COM_CRC_ERROR (1u << 15)/* er, b */ +#define R5_ILLEGAL_COMMAND (1u << 14)/* er, b */ +#define R5_IO_CURRENT_STATE_MASK (3u << 12)/* s, b */ +#define R5_IO_CURRENT_STATE(x) (((x) & R5_IO_CURRENT_STATE_MASK) >> 12) +#define R5_ERROR (1u << 11)/* erx, c */ +#define R5_FUNCTION_NUMBER (1u << 9)/* er, c */ +#define R5_OUT_OF_RANGE (1u << 8)/* er, c */ struct mmc_data { size_t len; /* size of the data */ size_t xfer_len; @@ -176,10 +205,12 @@ struct mmc_request { struct mmc_command *stop; void (*done)(struct mmc_request *); /* Completion function */ void *done_data; /* requestor set data */ -#ifndef __rtems__ uint32_t flags; +#ifndef __rtems__ #define MMC_REQ_DONE 1 -#else /* __rtems__ */ +#endif /* __rtems__ */ +#define MMC_TUNE_DONE 2 +#ifdef __rtems__ rtems_binary_semaphore req_done; #endif /* __rtems__ */ }; @@ -194,6 +225,7 @@ struct mmc_request { #define SD_SEND_RELATIVE_ADDR 3 #define MMC_SET_DSR 4 #define MMC_SLEEP_AWAKE 5 +#define IO_SEND_OP_COND 5 #define MMC_SWITCH_FUNC 6 #define MMC_SWITCH_FUNC_CMDS 0 #define MMC_SWITCH_FUNC_SET 1 @@ -276,7 +308,31 @@ struct mmc_request { /* Class 9: I/O cards (sd) */ #define SD_IO_RW_DIRECT 52 +/* CMD52 arguments */ +#define SD_ARG_CMD52_READ (0<<31) +#define SD_ARG_CMD52_WRITE (1<<31) +#define SD_ARG_CMD52_FUNC_SHIFT 28 +#define SD_ARG_CMD52_FUNC_MASK 0x7 +#define SD_ARG_CMD52_EXCHANGE (1<<27) +#define SD_ARG_CMD52_REG_SHIFT 9 +#define SD_ARG_CMD52_REG_MASK 0x1ffff +#define SD_ARG_CMD52_DATA_SHIFT 0 +#define SD_ARG_CMD52_DATA_MASK 0xff +#define SD_R5_DATA(resp) ((resp)[0] & 0xff) + #define SD_IO_RW_EXTENDED 53 +/* CMD53 arguments */ +#define SD_ARG_CMD53_READ (0<<31) +#define SD_ARG_CMD53_WRITE (1<<31) +#define SD_ARG_CMD53_FUNC_SHIFT 28 +#define SD_ARG_CMD53_FUNC_MASK 0x7 +#define SD_ARG_CMD53_BLOCK_MODE (1<<27) +#define SD_ARG_CMD53_INCREMENT (1<<26) +#define SD_ARG_CMD53_REG_SHIFT 9 +#define SD_ARG_CMD53_REG_MASK 0x1ffff +#define SD_ARG_CMD53_LENGTH_SHIFT 0 +#define SD_ARG_CMD53_LENGTH_MASK 0x1ff +#define SD_ARG_CMD53_LENGTH_MAX 64 /* XXX should be 511? */ /* Class 10: Switch function commands */ #define SD_SWITCH_FUNC 6 @@ -384,8 +440,8 @@ struct mmc_request { #define EXT_CSD_HS_TIMING_BC 0 #define EXT_CSD_HS_TIMING_HS 1 -#define EXT_CSD_HS_TIMING_DDR200 2 -#define EXT_CSD_HS_TIMING_DDR400 3 +#define EXT_CSD_HS_TIMING_HS200 2 +#define EXT_CSD_HS_TIMING_HS400 3 #define EXT_CSD_HS_TIMING_DRV_STR_SHIFT 4 #define EXT_CSD_POWER_CLASS_8BIT_MASK 0xf0 @@ -401,7 +457,6 @@ struct mmc_request { #define EXT_CSD_CARD_TYPE_HS200_1_2V 0x0020 #define EXT_CSD_CARD_TYPE_HS400_1_8V 0x0040 #define EXT_CSD_CARD_TYPE_HS400_1_2V 0x0080 -#define EXT_CSD_CARD_TYPE_HS400ES 0x0100 #define EXT_CSD_BUS_WIDTH_1 0 #define EXT_CSD_BUS_WIDTH_4 1 @@ -410,6 +465,8 @@ struct mmc_request { #define EXT_CSD_BUS_WIDTH_8_DDR 6 #define EXT_CSD_BUS_WIDTH_ES 0x80 +#define EXT_CSD_STROBE_SUPPORT_EN 0x01 + #define MMC_TYPE_HS_26_MAX 26000000 #define MMC_TYPE_HS_52_MAX 52000000 #define MMC_TYPE_DDR52_MAX 52000000 @@ -447,6 +504,54 @@ struct mmc_request { /* Specifications require 400 kHz max. during ID phase. */ #define SD_MMC_CARD_ID_FREQUENCY 400000 +/* + * SDIO Direct & Extended I/O + */ +#define SD_IO_RW_WR (1u << 31) +#define SD_IO_RW_FUNC(x) (((x) & 0x7) << 28) +#define SD_IO_RW_RAW (1u << 27) +#define SD_IO_RW_INCR (1u << 26) +#define SD_IO_RW_ADR(x) (((x) & 0x1FFFF) << 9) +#define SD_IO_RW_DAT(x) (((x) & 0xFF) << 0) +#define SD_IO_RW_LEN(x) (((x) & 0xFF) << 0) + +#define SD_IOE_RW_LEN(x) (((x) & 0x1FF) << 0) +#define SD_IOE_RW_BLK (1u << 27) + +/* Card Common Control Registers (CCCR) */ +#define SD_IO_CCCR_START 0x00000 +#define SD_IO_CCCR_SIZE 0x100 +#define SD_IO_CCCR_FN_ENABLE 0x02 +#define SD_IO_CCCR_FN_READY 0x03 +#define SD_IO_CCCR_INT_ENABLE 0x04 +#define SD_IO_CCCR_INT_PENDING 0x05 +#define SD_IO_CCCR_CTL 0x06 +#define CCCR_CTL_RES (1<<3) +#define SD_IO_CCCR_BUS_WIDTH 0x07 +#define CCCR_BUS_WIDTH_4 (1<<1) +#define CCCR_BUS_WIDTH_1 (1<<0) +#define SD_IO_CCCR_CARDCAP 0x08 +#define SD_IO_CCCR_CISPTR 0x09 /* XXX 9-10, 10-11, or 9-12 */ + +/* Function Basic Registers (FBR) */ +#define SD_IO_FBR_START 0x00100 +#define SD_IO_FBR_SIZE 0x00700 + +/* Card Information Structure (CIS) */ +#define SD_IO_CIS_START 0x01000 +#define SD_IO_CIS_SIZE 0x17000 + +/* CIS tuple codes (based on PC Card 16) */ +#define SD_IO_CISTPL_VERS_1 0x15 +#define SD_IO_CISTPL_MANFID 0x20 +#define SD_IO_CISTPL_FUNCID 0x21 +#define SD_IO_CISTPL_FUNCE 0x22 +#define SD_IO_CISTPL_END 0xff + +/* CISTPL_FUNCID codes */ +/* OpenBSD incorrectly defines 0x0c as FUNCTION_WLAN */ +/* #define SDMMC_FUNCTION_WLAN 0x0c */ + /* OCR bits */ /* @@ -562,6 +667,10 @@ struct mmc_sd_status #define MMC_PART_GP_MAX 4 #define MMC_PART_MAX 8 +#define MMC_TUNING_MAX 64 /* Maximum tuning iterations */ +#define MMC_TUNING_LEN 64 /* Size of tuning data */ +#define MMC_TUNING_LEN_HS200 128 /* Size of tuning data in HS200 mode */ + /* * Older versions of the MMC standard had a variable sector size. However, * I've been able to find no old MMC or SD cards that have a non 512 diff --git a/freebsd/sys/dev/mmc/mmcsd.c b/freebsd/sys/dev/mmc/mmcsd.c index 5066e250..195feae2 100644 --- a/freebsd/sys/dev/mmc/mmcsd.c +++ b/freebsd/sys/dev/mmc/mmcsd.c @@ -108,7 +108,8 @@ __FBSDID("$FreeBSD$"); struct mmcsd_softc; struct mmcsd_part { - struct mtx part_mtx; + struct mtx disk_mtx; + struct mtx ioctl_mtx; struct mmcsd_softc *sc; #ifndef __rtems__ struct disk *disk; @@ -120,6 +121,7 @@ struct mmcsd_part { u_int type; int running; int suspend; + int ioctl; bool ro; char name[MMCSD_PART_NAMELEN]; }; @@ -129,6 +131,9 @@ struct mmcsd_softc { device_t mmcbr; struct mmcsd_part *part[MMC_PART_MAX]; enum mmc_card_mode mode; + u_int max_data; /* Maximum data size [blocks] */ + u_int erase_sector; /* Device native erase sector size [blocks] */ + uint8_t high_cap; /* High Capacity device (block addressed) */ uint8_t part_curr; /* Partition currently switched to */ uint8_t ext_csd[MMC_EXTCSD_SIZE]; uint16_t rca; @@ -199,15 +204,25 @@ static int mmcsd_slicer(device_t dev, const char *provider, static int mmcsd_switch_part(device_t bus, device_t dev, uint16_t rca, u_int part); -#define MMCSD_PART_LOCK(_part) mtx_lock(&(_part)->part_mtx) -#define MMCSD_PART_UNLOCK(_part) mtx_unlock(&(_part)->part_mtx) -#define MMCSD_PART_LOCK_INIT(_part) \ - mtx_init(&(_part)->part_mtx, (_part)->name, "mmcsd part", MTX_DEF) -#define MMCSD_PART_LOCK_DESTROY(_part) mtx_destroy(&(_part)->part_mtx); -#define MMCSD_PART_ASSERT_LOCKED(_part) \ - mtx_assert(&(_part)->part_mtx, MA_OWNED); -#define MMCSD_PART_ASSERT_UNLOCKED(_part) \ - mtx_assert(&(_part)->part_mtx, MA_NOTOWNED); +#define MMCSD_DISK_LOCK(_part) mtx_lock(&(_part)->disk_mtx) +#define MMCSD_DISK_UNLOCK(_part) mtx_unlock(&(_part)->disk_mtx) +#define MMCSD_DISK_LOCK_INIT(_part) \ + mtx_init(&(_part)->disk_mtx, (_part)->name, "mmcsd disk", MTX_DEF) +#define MMCSD_DISK_LOCK_DESTROY(_part) mtx_destroy(&(_part)->disk_mtx); +#define MMCSD_DISK_ASSERT_LOCKED(_part) \ + mtx_assert(&(_part)->disk_mtx, MA_OWNED); +#define MMCSD_DISK_ASSERT_UNLOCKED(_part) \ + mtx_assert(&(_part)->disk_mtx, MA_NOTOWNED); + +#define MMCSD_IOCTL_LOCK(_part) mtx_lock(&(_part)->ioctl_mtx) +#define MMCSD_IOCTL_UNLOCK(_part) mtx_unlock(&(_part)->ioctl_mtx) +#define MMCSD_IOCTL_LOCK_INIT(_part) \ + mtx_init(&(_part)->ioctl_mtx, (_part)->name, "mmcsd IOCTL", MTX_DEF) +#define MMCSD_IOCTL_LOCK_DESTROY(_part) mtx_destroy(&(_part)->ioctl_mtx); +#define MMCSD_IOCTL_ASSERT_LOCKED(_part) \ + mtx_assert(&(_part)->ioctl_mtx, MA_OWNED); +#define MMCSD_IOCLT_ASSERT_UNLOCKED(_part) \ + mtx_assert(&(_part)->ioctl_mtx, MA_NOTOWNED); static int mmcsd_probe(device_t dev) @@ -277,7 +292,7 @@ rtems_bsd_mmcsd_disk_read_write(struct mmcsd_part *part, rtems_blkdev_request *b data_flags = MMC_DATA_READ; } - MMCSD_PART_LOCK(part); + MMCSD_DISK_LOCK(part); for (i = 0; i < buffer_count; ++i) { rtems_blkdev_sg_buffer *sg = &blkreq->bufs [i]; @@ -354,7 +369,7 @@ rtems_bsd_mmcsd_disk_read_write(struct mmcsd_part *part, rtems_blkdev_request *b error: - MMCSD_PART_UNLOCK(part); + MMCSD_DISK_UNLOCK(part); rtems_blkdev_request_done(blkreq, status_code); @@ -439,6 +454,18 @@ mmcsd_attach(device_t dev) sc->dev = dev; sc->mmcbr = mmcbr = device_get_parent(dev); sc->mode = mmcbr_get_mode(mmcbr); + /* + * Note that in principle with an SDHCI-like re-tuning implementation, + * the maximum data size can change at runtime due to a device removal/ + * insertion that results in switches to/from a transfer mode involving + * re-tuning, iff there are multiple devices on a given bus. Until now + * mmc(4) lacks support for rescanning already attached buses, however, + * and sdhci(4) to date has no support for shared buses in the first + * place either. + */ + sc->max_data = mmc_get_max_data(dev); + sc->erase_sector = mmc_get_erase_sector(dev); + sc->high_cap = mmc_get_high_cap(dev); sc->rca = mmc_get_rca(dev); /* Only MMC >= 4.x devices support EXT_CSD. */ @@ -492,7 +519,7 @@ mmcsd_attach(device_t dev) (ext_csd[EXT_CSD_ENH_START_ADDR + 1] << 8) + (ext_csd[EXT_CSD_ENH_START_ADDR + 2] << 16) + (ext_csd[EXT_CSD_ENH_START_ADDR + 3] << 24)) * - (mmc_get_high_cap(dev) ? MMC_SECTOR_SIZE : 1); + (sc->high_cap != 0 ? MMC_SECTOR_SIZE : 1); } else if (bootverbose) device_printf(dev, "enhanced user data area spans entire device\n"); @@ -505,7 +532,7 @@ mmcsd_attach(device_t dev) ro = mmc_get_read_only(dev); mmcsd_add_part(sc, EXT_CSD_PART_CONFIG_ACC_DEFAULT, "mmcsd", device_get_unit(dev), mmc_get_media_size(dev) * sector_size, - mmc_get_erase_sector(dev) * sector_size, ro); + sc->erase_sector * sector_size, ro); if (mmc_get_spec_vers(dev) < 3) return (0); @@ -644,7 +671,16 @@ mmcsd_add_part(struct mmcsd_softc *sc, u_int type, const char *name, u_int cnt, part->ro = ro; snprintf(part->name, sizeof(part->name), name, device_get_unit(dev)); - /* For the RPMB partition, allow IOCTL access only. */ + MMCSD_IOCTL_LOCK_INIT(part); + + /* + * For the RPMB partition, allow IOCTL access only. + * NB: If ever attaching RPMB partitions to disk(9), the re-tuning + * implementation and especially its pausing need to be revisited, + * because then re-tuning requests may be issued by the IOCTL half + * of this driver while re-tuning is already paused by the disk(9) + * one and vice versa. + */ if (type == EXT_CSD_PART_CONFIG_ACC_RPMB) { make_dev_args_init(&args); args.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK; @@ -659,7 +695,7 @@ mmcsd_add_part(struct mmcsd_softc *sc, u_int type, const char *name, u_int cnt, return; } } else { - MMCSD_PART_LOCK_INIT(part); + MMCSD_DISK_LOCK_INIT(part); #ifndef __rtems__ d = part->disk = disk_alloc(); @@ -672,7 +708,7 @@ mmcsd_add_part(struct mmcsd_softc *sc, u_int type, const char *name, u_int cnt, d->d_name = part->name; d->d_drv1 = part; d->d_sectorsize = mmc_get_sector_size(dev); - d->d_maxsize = mmc_get_max_data(dev) * d->d_sectorsize; + d->d_maxsize = sc->max_data * d->d_sectorsize; d->d_mediasize = media_size; d->d_stripesize = erase_size; d->d_unit = cnt; @@ -704,7 +740,7 @@ mmcsd_add_part(struct mmcsd_softc *sc, u_int type, const char *name, u_int cnt, part->name, cnt, bytes, unit, mmc_get_card_id_string(dev), ro ? " (read-only)" : "", device_get_nameunit(mmcbr), speed / 1000000, (speed / 100000) % 10, - mmcsd_bus_bit_width(dev), mmc_get_max_data(dev)); + mmcsd_bus_bit_width(dev), sc->max_data); } else if (type == EXT_CSD_PART_CONFIG_ACC_RPMB) { printf("%s: %ju%sB partion %d%s at %s\n", part->name, bytes, unit, type, ro ? " (read-only)" : "", @@ -795,19 +831,27 @@ mmcsd_detach(device_t dev) for (i = 0; i < MMC_PART_MAX; i++) { part = sc->part[i]; - if (part != NULL && part->disk != NULL) { - MMCSD_PART_LOCK(part); - part->suspend = 0; - if (part->running > 0) { - /* kill thread */ - part->running = 0; - wakeup(part); - /* wait for thread to finish. */ - while (part->running != -1) - msleep(part, &part->part_mtx, 0, - "detach", 0); + if (part != NULL) { + if (part->disk != NULL) { + MMCSD_DISK_LOCK(part); + part->suspend = 0; + if (part->running > 0) { + /* kill thread */ + part->running = 0; + wakeup(part); + /* wait for thread to finish. */ + while (part->running != -1) + msleep(part, &part->disk_mtx, 0, + "mmcsd disk detach", 0); + } + MMCSD_DISK_UNLOCK(part); } - MMCSD_PART_UNLOCK(part); + MMCSD_IOCTL_LOCK(part); + while (part->ioctl > 0) + msleep(part, &part->ioctl_mtx, 0, + "mmcsd IOCTL detach", 0); + part->ioctl = -1; + MMCSD_IOCTL_UNLOCK(part); } } @@ -823,8 +867,9 @@ mmcsd_detach(device_t dev) /* kill disk */ disk_destroy(part->disk); - MMCSD_PART_LOCK_DESTROY(part); + MMCSD_DISK_LOCK_DESTROY(part); } + MMCSD_IOCTL_LOCK_DESTROY(part); free(part, M_DEVBUF); } } @@ -844,19 +889,27 @@ mmcsd_suspend(device_t dev) for (i = 0; i < MMC_PART_MAX; i++) { part = sc->part[i]; - if (part != NULL && part->disk != NULL) { - MMCSD_PART_LOCK(part); - part->suspend = 1; - if (part->running > 0) { - /* kill thread */ - part->running = 0; - wakeup(part); - /* wait for thread to finish. */ - while (part->running != -1) - msleep(part, &part->part_mtx, 0, - "detach", 0); + if (part != NULL) { + if (part->disk != NULL) { + MMCSD_DISK_LOCK(part); + part->suspend = 1; + if (part->running > 0) { + /* kill thread */ + part->running = 0; + wakeup(part); + /* wait for thread to finish. */ + while (part->running != -1) + msleep(part, &part->disk_mtx, 0, + "mmcsd disk suspension", 0); + } + MMCSD_DISK_UNLOCK(part); } - MMCSD_PART_UNLOCK(part); + MMCSD_IOCTL_LOCK(part); + while (part->ioctl > 0) + msleep(part, &part->ioctl_mtx, 0, + "mmcsd IOCTL suspension", 0); + part->ioctl = -1; + MMCSD_IOCTL_UNLOCK(part); } } #else /* __rtems__ */ @@ -875,16 +928,22 @@ mmcsd_resume(device_t dev) for (i = 0; i < MMC_PART_MAX; i++) { part = sc->part[i]; - if (part != NULL && part->disk != NULL) { - MMCSD_PART_LOCK(part); - part->suspend = 0; - if (part->running <= 0) { - part->running = 1; - kproc_create(&mmcsd_task, part, &part->p, 0, 0, - "%s%d: mmc/sd card", part->name, part->cnt); - MMCSD_PART_UNLOCK(part); - } else - MMCSD_PART_UNLOCK(part); + if (part != NULL) { + if (part->disk != NULL) { + MMCSD_DISK_LOCK(part); + part->suspend = 0; + if (part->running <= 0) { + part->running = 1; + MMCSD_DISK_UNLOCK(part); + kproc_create(&mmcsd_task, part, + &part->p, 0, 0, "%s%d: mmc/sd card", + part->name, part->cnt); + } else + MMCSD_DISK_UNLOCK(part); + } + MMCSD_IOCTL_LOCK(part); + part->ioctl = 0; + MMCSD_IOCTL_UNLOCK(part); } } #else /* __rtems__ */ @@ -916,13 +975,13 @@ mmcsd_strategy(struct bio *bp) part = bp->bio_disk->d_drv1; sc = part->sc; - MMCSD_PART_LOCK(part); + MMCSD_DISK_LOCK(part); if (part->running > 0 || part->suspend > 0) { bioq_disksort(&part->bio_queue, bp); - MMCSD_PART_UNLOCK(part); + MMCSD_DISK_UNLOCK(part); wakeup(part); } else { - MMCSD_PART_UNLOCK(part); + MMCSD_DISK_UNLOCK(part); biofinish(bp, NULL, ENXIO); } } @@ -961,9 +1020,9 @@ mmcsd_ioctl(struct mmcsd_part *part, u_long cmd, void *data, int fflag) switch (cmd) { case MMC_IOC_CMD: mic = data; - err = mmcsd_ioctl_cmd(part, data, fflag); + err = mmcsd_ioctl_cmd(part, mic, fflag); break; - case MMC_IOC_CMD_MULTI: + case MMC_IOC_MULTI_CMD: mimc = data; if (mimc->num_of_cmds == 0) break; @@ -973,12 +1032,12 @@ mmcsd_ioctl(struct mmcsd_part *part, u_long cmd, void *data, int fflag) size = sizeof(*mic) * cnt; mic = malloc(size, M_TEMP, M_WAITOK); err = copyin((const void *)mimc->cmds, mic, size); - if (err != 0) - break; - for (i = 0; i < cnt; i++) { - err = mmcsd_ioctl_cmd(part, &mic[i], fflag); - if (err != 0) - break; + if (err == 0) { + for (i = 0; i < cnt; i++) { + err = mmcsd_ioctl_cmd(part, &mic[i], fflag); + if (err != 0) + break; + } } free(mic, M_TEMP); break; @@ -1007,11 +1066,31 @@ mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_ioc_cmd *mic, int fflag) if (part->ro == TRUE && mic->write_flag != 0) return (EROFS); + /* + * We don't need to explicitly lock against the disk(9) half of this + * driver as MMCBUS_ACQUIRE_BUS() will serialize us. However, it's + * necessary to protect against races with detachment and suspension, + * especially since it's required to switch away from RPMB partitions + * again after an access (see mmcsd_switch_part()). + */ + MMCSD_IOCTL_LOCK(part); + while (part->ioctl != 0) { + if (part->ioctl < 0) { + MMCSD_IOCTL_UNLOCK(part); + return (ENXIO); + } + msleep(part, &part->ioctl_mtx, 0, "mmcsd IOCTL", 0); + } + part->ioctl = 1; + MMCSD_IOCTL_UNLOCK(part); + err = 0; dp = NULL; len = mic->blksz * mic->blocks; - if (len > MMC_IOC_MAX_BYTES) - return (EOVERFLOW); + if (len > MMC_IOC_MAX_BYTES) { + err = EOVERFLOW; + goto out; + } if (len != 0) { dp = malloc(len, M_TEMP, M_WAITOK); err = copyin((void *)(uintptr_t)mic->data_ptr, dp, len); @@ -1066,7 +1145,7 @@ mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_ioc_cmd *mic, int fflag) err = mmcsd_set_blockcount(sc, mic->blocks, mic->write_flag & (1 << 31)); if (err != MMC_ERR_NONE) - goto release; + goto switch_back; } if (mic->is_acmd != 0) (void)mmc_wait_for_app_cmd(mmcbr, dev, rca, &cmd, 0); @@ -1088,6 +1167,7 @@ mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_ioc_cmd *mic, int fflag) DELAY(1000); } while (retries-- > 0); +switch_back: /* ... and always switch back to the default partition. */ err = mmcsd_switch_part(mmcbr, dev, rca, EXT_CSD_PART_CONFIG_ACC_DEFAULT); @@ -1138,6 +1218,10 @@ release: err = EIO; out: + MMCSD_IOCTL_LOCK(part); + part->ioctl = 0; + MMCSD_IOCTL_UNLOCK(part); + wakeup(part); if (dp != NULL) free(dp, M_TEMP); return (err); @@ -1191,10 +1275,23 @@ mmcsd_switch_part(device_t bus, device_t dev, uint16_t rca, u_int part) sc = device_get_softc(dev); - if (sc->part_curr == part) + if (sc->mode == mode_sd) return (MMC_ERR_NONE); - if (sc->mode == mode_sd) + /* + * According to section "6.2.2 Command restrictions" of the eMMC + * specification v5.1, CMD19/CMD21 aren't allowed to be used with + * RPMB partitions. So we pause re-tuning along with triggering + * it up-front to decrease the likelihood of re-tuning becoming + * necessary while accessing an RPMB partition. Consequently, an + * RPMB partition should immediately be switched away from again + * after an access in order to allow for re-tuning to take place + * anew. + */ + if (part == EXT_CSD_PART_CONFIG_ACC_RPMB) + MMCBUS_RETUNE_PAUSE(sc->mmcbr, sc->dev, true); + + if (sc->part_curr == part) return (MMC_ERR_NONE); value = (sc->ext_csd[EXT_CSD_PART_CONFIG] & @@ -1202,10 +1299,15 @@ mmcsd_switch_part(device_t bus, device_t dev, uint16_t rca, u_int part) /* Jump! */ err = mmc_switch(bus, dev, rca, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONFIG, value, sc->part_time, true); - if (err != MMC_ERR_NONE) + if (err != MMC_ERR_NONE) { + if (part == EXT_CSD_PART_CONFIG_ACC_RPMB) + MMCBUS_RETUNE_UNPAUSE(sc->mmcbr, sc->dev); return (err); + } sc->ext_csd[EXT_CSD_PART_CONFIG] = value; + if (sc->part_curr == EXT_CSD_PART_CONFIG_ACC_RPMB) + MMCBUS_RETUNE_UNPAUSE(sc->mmcbr, sc->dev); sc->part_curr = part; return (MMC_ERR_NONE); } @@ -1217,7 +1319,7 @@ mmcsd_errmsg(int e) if (e < 0 || e > MMC_ERR_MAX) return "Bad error code"; - return errmsg[e]; + return (errmsg[e]); } static daddr_t @@ -1230,7 +1332,7 @@ mmcsd_rw(struct mmcsd_part *part, struct bio *bp) struct mmc_data data; struct mmcsd_softc *sc; device_t dev, mmcbr; - int numblocks, sz; + u_int numblocks, sz; char *vaddr; sc = part->sc; @@ -1242,7 +1344,7 @@ mmcsd_rw(struct mmcsd_part *part, struct bio *bp) end = bp->bio_pblkno + (bp->bio_bcount / sz); while (block < end) { vaddr = bp->bio_data + (block - bp->bio_pblkno) * sz; - numblocks = min(end - block, mmc_get_max_data(dev)); + numblocks = min(end - block, sc->max_data); memset(&req, 0, sizeof(req)); memset(&cmd, 0, sizeof(cmd)); memset(&stop, 0, sizeof(stop)); @@ -1262,7 +1364,7 @@ mmcsd_rw(struct mmcsd_part *part, struct bio *bp) cmd.opcode = MMC_WRITE_BLOCK; } cmd.arg = block; - if (!mmc_get_high_cap(dev)) + if (sc->high_cap == 0) cmd.arg <<= 9; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; data.data = vaddr; @@ -1302,7 +1404,7 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp) struct mmc_request req; struct mmcsd_softc *sc; device_t dev, mmcbr; - int erase_sector, sz; + u_int erase_sector, sz; sc = part->sc; dev = sc->dev; @@ -1317,7 +1419,7 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp) if (end >= part->eblock && end < part->eend) end = part->eend; /* Safe round to the erase sector boundaries. */ - erase_sector = mmc_get_erase_sector(dev); + erase_sector = sc->erase_sector; start = block + erase_sector - 1; /* Round up. */ start -= start % erase_sector; stop = end; /* Round down. */ @@ -1329,6 +1431,12 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp) return (end); } + /* + * Pause re-tuning so it won't interfere with the order of erase + * commands. Note that these latter don't use the data lines, so + * re-tuning shouldn't actually become necessary during erase. + */ + MMCBUS_RETUNE_PAUSE(mmcbr, dev, false); /* Set erase start position. */ memset(&req, 0, sizeof(req)); memset(&cmd, 0, sizeof(cmd)); @@ -1339,13 +1447,15 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp) else cmd.opcode = MMC_ERASE_GROUP_START; cmd.arg = start; - if (!mmc_get_high_cap(dev)) + if (sc->high_cap == 0) cmd.arg <<= 9; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; MMCBUS_WAIT_FOR_REQUEST(mmcbr, dev, &req); if (req.cmd->error != MMC_ERR_NONE) { - printf("erase err1: %d\n", req.cmd->error); - return (block); + device_printf(dev, "Setting erase start position failed %d\n", + req.cmd->error); + block = bp->bio_pblkno; + goto unpause; } /* Set erase stop position. */ memset(&req, 0, sizeof(req)); @@ -1356,14 +1466,16 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp) else cmd.opcode = MMC_ERASE_GROUP_END; cmd.arg = stop; - if (!mmc_get_high_cap(dev)) + if (sc->high_cap == 0) cmd.arg <<= 9; cmd.arg--; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; MMCBUS_WAIT_FOR_REQUEST(mmcbr, dev, &req); if (req.cmd->error != MMC_ERR_NONE) { - printf("erase err2: %d\n", req.cmd->error); - return (block); + device_printf(dev, "Setting erase stop position failed %d\n", + req.cmd->error); + block = bp->bio_pblkno; + goto unpause; } /* Erase range. */ memset(&req, 0, sizeof(req)); @@ -1374,8 +1486,11 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp) cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; MMCBUS_WAIT_FOR_REQUEST(mmcbr, dev, &req); if (req.cmd->error != MMC_ERR_NONE) { - printf("erase err3 %d\n", req.cmd->error); - return (block); + device_printf(dev, "erase err3: %d\n", req.cmd->error); + device_printf(dev, "Issuing erase command failed %d\n", + req.cmd->error); + block = bp->bio_pblkno; + goto unpause; } /* Store one of remaining parts for the next call. */ if (bp->bio_pblkno >= part->eblock || block == start) { @@ -1385,7 +1500,10 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp) part->eblock = block; /* Predict next backward. */ part->eend = start; } - return (end); + block = end; +unpause: + MMCBUS_RETUNE_UNPAUSE(mmcbr, dev); + return (block); } static int @@ -1446,16 +1564,16 @@ mmcsd_task(void *arg) mmcbr = sc->mmcbr; while (1) { - MMCSD_PART_LOCK(part); + MMCSD_DISK_LOCK(part); do { if (part->running == 0) goto out; bp = bioq_takefirst(&part->bio_queue); if (bp == NULL) - msleep(part, &part->part_mtx, PRIBIO, - "jobqueue", 0); + msleep(part, &part->disk_mtx, PRIBIO, + "mmcsd disk jobqueue", 0); } while (bp == NULL); - MMCSD_PART_UNLOCK(part); + MMCSD_DISK_UNLOCK(part); if (bp->bio_cmd != BIO_READ && part->ro) { bp->bio_error = EROFS; bp->bio_resid = bp->bio_bcount; @@ -1496,7 +1614,7 @@ release: out: /* tell parent we're done */ part->running = -1; - MMCSD_PART_UNLOCK(part); + MMCSD_DISK_UNLOCK(part); wakeup(part); kproc_exit(0); diff --git a/freebsd/sys/dev/nvme/nvme.h b/freebsd/sys/dev/nvme/nvme.h index ff64cb00..aa640b37 100644 --- a/freebsd/sys/dev/nvme/nvme.h +++ b/freebsd/sys/dev/nvme/nvme.h @@ -341,9 +341,11 @@ enum nvme_admin_opcode { NVME_OPC_GET_FEATURES = 0x0a, /* 0x0b - reserved */ NVME_OPC_ASYNC_EVENT_REQUEST = 0x0c, - /* 0x0d-0x0f - reserved */ + NVME_OPC_NAMESPACE_MANAGEMENT = 0x0d, + /* 0x0e-0x0f - reserved */ NVME_OPC_FIRMWARE_ACTIVATE = 0x10, NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD = 0x11, + NVME_OPC_NAMESPACE_ATTACHMENT = 0x15, NVME_OPC_FORMAT_NVM = 0x80, NVME_OPC_SECURITY_SEND = 0x81, @@ -456,7 +458,10 @@ struct nvme_controller_data { /** maximum data transfer size */ uint8_t mdts; - uint8_t reserved1[178]; + /** Controller ID */ + uint16_t ctrlr_id; + + uint8_t reserved1[176]; /* bytes 256-511: admin command set attributes */ @@ -471,7 +476,10 @@ struct nvme_controller_data { /* supports firmware activate/download commands */ uint16_t firmware : 1; - uint16_t oacs_rsvd : 13; + /* supports namespace management commands */ + uint16_t nsmgmt : 1; + + uint16_t oacs_rsvd : 12; } __packed oacs; /** abort command limit */ @@ -513,8 +521,16 @@ struct nvme_controller_data { uint8_t avscc_rsvd : 7; } __packed avscc; - uint8_t reserved2[247]; + uint8_t reserved2[15]; + + /** Name space capabilities */ + struct { + /* if nsmgmt, report tnvmcap and unvmcap */ + uint8_t tnvmcap[16]; + uint8_t unvmcap[16]; + } __packed untncap; + uint8_t reserved3[200]; /* bytes 512-703: nvm command set attributes */ /** submission queue entry size */ @@ -529,7 +545,7 @@ struct nvme_controller_data { uint8_t max : 4; } __packed cqes; - uint8_t reserved3[2]; + uint8_t reserved4[2]; /** number of namespaces */ uint32_t nn; @@ -555,10 +571,10 @@ struct nvme_controller_data { } __packed vwc; /* TODO: flesh out remaining nvm command set attributes */ - uint8_t reserved4[178]; + uint8_t reserved5[178]; /* bytes 704-2047: i/o command set attributes */ - uint8_t reserved5[1344]; + uint8_t reserved6[1344]; /* bytes 2048-3071: power state descriptors */ struct nvme_power_state power_state[32]; diff --git a/freebsd/sys/dev/rtwn/if_rtwn_rx.c b/freebsd/sys/dev/rtwn/if_rtwn_rx.c index 5dd72605..7b3f1c2e 100644 --- a/freebsd/sys/dev/rtwn/if_rtwn_rx.c +++ b/freebsd/sys/dev/rtwn/if_rtwn_rx.c @@ -55,7 +55,6 @@ __FBSDID("$FreeBSD$"); #include <dev/rtwn/if_rtwn_rx.h> #include <dev/rtwn/rtl8192c/r92c_reg.h> -#include <dev/rtwn/rtl8192c/r92c_rx_desc.h> void @@ -192,7 +191,8 @@ rtwn_get_tsf(struct rtwn_softc *sc, uint64_t *buf, int id) } static uint64_t -rtwn_extend_rx_tsf(struct rtwn_softc *sc, const struct r92c_rx_stat *stat) +rtwn_extend_rx_tsf(struct rtwn_softc *sc, + const struct rtwn_rx_stat_common *stat) { uint64_t tsft; uint32_t rxdw3, tsfl, tsfl_curr; @@ -200,7 +200,7 @@ rtwn_extend_rx_tsf(struct rtwn_softc *sc, const struct r92c_rx_stat *stat) rxdw3 = le32toh(stat->rxdw3); tsfl = le32toh(stat->tsf_low); - id = MS(rxdw3, R92C_RXDW3_BSSID_FIT); + id = MS(rxdw3, RTWN_RXDW3_BSSID01_FIT); switch (id) { case 1: @@ -243,7 +243,7 @@ rtwn_rx_common(struct rtwn_softc *sc, struct mbuf *m, void *desc) struct ieee80211_frame_min *wh; struct ieee80211_rx_stats rxs; struct rtwn_node *un; - struct r92c_rx_stat *stat; + struct rtwn_rx_stat_common *stat; void *physt; uint32_t rxdw0; int8_t rssi; @@ -252,10 +252,10 @@ rtwn_rx_common(struct rtwn_softc *sc, struct mbuf *m, void *desc) stat = desc; rxdw0 = le32toh(stat->rxdw0); - cipher = MS(rxdw0, R92C_RXDW0_CIPHER); - infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8; - pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN); - shift = MS(rxdw0, R92C_RXDW0_SHIFT); + cipher = MS(rxdw0, RTWN_RXDW0_CIPHER); + infosz = MS(rxdw0, RTWN_RXDW0_INFOSZ) * 8; + pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN); + shift = MS(rxdw0, RTWN_RXDW0_SHIFT); wh = (struct ieee80211_frame_min *)(mtodo(m, shift + infosz)); if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && @@ -270,7 +270,7 @@ rtwn_rx_common(struct rtwn_softc *sc, struct mbuf *m, void *desc) ni = NULL; un = RTWN_NODE(ni); - if (infosz != 0 && (rxdw0 & R92C_RXDW0_PHYST)) + if (infosz != 0 && (rxdw0 & RTWN_RXDW0_PHYST)) physt = (void *)mtodo(m, shift); else physt = (un != NULL) ? &un->last_physt : &sc->last_physt; @@ -286,7 +286,7 @@ rtwn_rx_common(struct rtwn_softc *sc, struct mbuf *m, void *desc) /* Add some common bits. */ /* NB: should not happen. */ - if (rxdw0 & R92C_RXDW0_CRCERR) + if (rxdw0 & RTWN_RXDW0_CRCERR) rxs.c_pktflags |= IEEE80211_RX_F_FAIL_FCSCRC; rxs.r_flags |= IEEE80211_R_TSF_START; /* XXX undocumented */ @@ -300,7 +300,7 @@ rtwn_rx_common(struct rtwn_softc *sc, struct mbuf *m, void *desc) /* XXX TODO: we really need a rate-to-string method */ RTWN_DPRINTF(sc, RTWN_DEBUG_RSSI, "%s: rssi %d, rate %d\n", __func__, rssi, rxs.c_rate); - if (un != NULL && infosz != 0 && (rxdw0 & R92C_RXDW0_PHYST)) { + if (un != NULL && infosz != 0 && (rxdw0 & RTWN_RXDW0_PHYST)) { /* Update our average RSSI. */ rtwn_update_avgrssi(sc, un, rssi, is_cck); } diff --git a/freebsd/sys/dev/rtwn/if_rtwnreg.h b/freebsd/sys/dev/rtwn/if_rtwnreg.h index 9dc830a2..00903d88 100644 --- a/freebsd/sys/dev/rtwn/if_rtwnreg.h +++ b/freebsd/sys/dev/rtwn/if_rtwnreg.h @@ -18,6 +18,9 @@ * $FreeBSD$ */ +#ifndef IF_RTWNREG_H +#define IF_RTWNREG_H + #define R92C_MIN_TX_PWR 0x00 #define R92C_MAX_TX_PWR 0x3f @@ -48,6 +51,55 @@ struct rtwn_tx_desc_common { } txdw7; } __packed __attribute__((aligned(4))); +/* Common part of Rx descriptor. */ +struct rtwn_rx_stat_common { + uint32_t rxdw0; +#define RTWN_RXDW0_PKTLEN_M 0x00003fff +#define RTWN_RXDW0_PKTLEN_S 0 +#define RTWN_RXDW0_CRCERR 0x00004000 +#define RTWN_RXDW0_ICVERR 0x00008000 +#define RTWN_RXDW0_INFOSZ_M 0x000f0000 +#define RTWN_RXDW0_INFOSZ_S 16 +#define RTWN_RXDW0_CIPHER_M 0x00700000 +#define RTWN_RXDW0_CIPHER_S 20 +#define RTWN_RXDW0_QOS 0x00800000 +#define RTWN_RXDW0_SHIFT_M 0x03000000 +#define RTWN_RXDW0_SHIFT_S 24 +#define RTWN_RXDW0_PHYST 0x04000000 +#define RTWN_RXDW0_SWDEC 0x08000000 +#define RTWN_RXDW0_LS 0x10000000 +#define RTWN_RXDW0_FS 0x20000000 +#define RTWN_RXDW0_EOR 0x40000000 +#define RTWN_RXDW0_OWN 0x80000000 + + uint32_t rxdw1; +#define RTWN_RXDW1_AMSDU 0x00002000 +#define RTWN_RXDW1_MC 0x40000000 +#define RTWN_RXDW1_BC 0x80000000 + + uint32_t rxdw2; + uint32_t rxdw3; +#define RTWN_RXDW3_HTC 0x00000400 +#define RTWN_RXDW3_BSSID01_FIT_M 0x00003000 +#define RTWN_RXDW3_BSSID01_FIT_S 12 + + uint32_t rxdw4; + uint32_t tsf_low; +} __packed __attribute__((aligned(4))); + +/* Rx descriptor for PCIe devices. */ +struct rtwn_rx_stat_pci { + uint32_t rxdw0; + uint32_t rxdw1; + uint32_t rxdw2; + uint32_t rxdw3; + uint32_t rxdw4; + uint32_t tsf_low; + + uint32_t rxbufaddr; + uint32_t rxbufaddr64; +} __packed __attribute__((aligned(4))); + /* * Macros to access subfields in registers. */ @@ -116,3 +168,5 @@ rtwn_chan2centieee(const struct ieee80211_channel *c) return (chan); } + +#endif /* IF_RTWNREG_H */ diff --git a/freebsd/sys/dev/rtwn/if_rtwnvar.h b/freebsd/sys/dev/rtwn/if_rtwnvar.h index d8754024..3ebcba52 100644 --- a/freebsd/sys/dev/rtwn/if_rtwnvar.h +++ b/freebsd/sys/dev/rtwn/if_rtwnvar.h @@ -25,8 +25,6 @@ #define RTWN_TX_DESC_SIZE 64 -#define RTWN_TXBUFSZ (16 * 1024) - #define RTWN_BCN_MAX_SIZE 512 #define RTWN_CAM_ENTRY_LIMIT 64 diff --git a/freebsd/sys/dev/rtwn/pci/rtwn_pci_attach.c b/freebsd/sys/dev/rtwn/pci/rtwn_pci_attach.c index 85ede40a..c121c5a5 100644 --- a/freebsd/sys/dev/rtwn/pci/rtwn_pci_attach.c +++ b/freebsd/sys/dev/rtwn/pci/rtwn_pci_attach.c @@ -64,7 +64,6 @@ __FBSDID("$FreeBSD$"); #include <dev/rtwn/pci/rtwn_pci_tx.h> #include <dev/rtwn/rtl8192c/pci/r92ce_reg.h> -#include <dev/rtwn/rtl8192c/pci/r92ce_rx_desc.h> static device_probe_t rtwn_pci_probe; @@ -135,7 +134,7 @@ rtwn_pci_alloc_rx_list(struct rtwn_softc *sc) int i, error; /* Allocate Rx descriptors. */ - size = sizeof(struct r92ce_rx_stat) * RTWN_PCI_RX_LIST_COUNT; + size = sizeof(struct rtwn_rx_stat_pci) * RTWN_PCI_RX_LIST_COUNT; error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, 1, size, 0, NULL, NULL, &rx_ring->desc_dmat); diff --git a/freebsd/sys/dev/rtwn/pci/rtwn_pci_rx.c b/freebsd/sys/dev/rtwn/pci/rtwn_pci_rx.c index 150500d8..1934b741 100644 --- a/freebsd/sys/dev/rtwn/pci/rtwn_pci_rx.c +++ b/freebsd/sys/dev/rtwn/pci/rtwn_pci_rx.c @@ -58,8 +58,6 @@ __FBSDID("$FreeBSD$"); #include <dev/rtwn/pci/rtwn_pci_var.h> #include <dev/rtwn/pci/rtwn_pci_rx.h> -#include <dev/rtwn/rtl8192c/pci/r92ce_rx_desc.h> - void rtwn_pci_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, @@ -73,21 +71,21 @@ rtwn_pci_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, } void -rtwn_pci_setup_rx_desc(struct rtwn_pci_softc *pc, struct r92ce_rx_stat *desc, - bus_addr_t addr, size_t len, int idx) +rtwn_pci_setup_rx_desc(struct rtwn_pci_softc *pc, + struct rtwn_rx_stat_pci *desc, bus_addr_t addr, size_t len, int idx) { memset(desc, 0, sizeof(*desc)); - desc->rxdw0 = htole32(SM(R92C_RXDW0_PKTLEN, len) | - ((idx == RTWN_PCI_RX_LIST_COUNT - 1) ? R92C_RXDW0_EOR : 0)); + desc->rxdw0 = htole32(SM(RTWN_RXDW0_PKTLEN, len) | + ((idx == RTWN_PCI_RX_LIST_COUNT - 1) ? RTWN_RXDW0_EOR : 0)); desc->rxbufaddr = htole32(addr); bus_space_barrier(pc->pc_st, pc->pc_sh, 0, pc->pc_mapsize, BUS_SPACE_BARRIER_WRITE); - desc->rxdw0 |= htole32(R92C_RXDW0_OWN); + desc->rxdw0 |= htole32(RTWN_RXDW0_OWN); } static void -rtwn_pci_rx_frame(struct rtwn_softc *sc, struct r92ce_rx_stat *rx_desc, +rtwn_pci_rx_frame(struct rtwn_softc *sc, struct rtwn_rx_stat_pci *rx_desc, int desc_idx) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); @@ -109,18 +107,18 @@ rtwn_pci_rx_frame(struct rtwn_softc *sc, struct r92ce_rx_stat *rx_desc, le32toh(rx_desc->rxbufaddr), le32toh(rx_desc->rxbufaddr64)); rxdw0 = le32toh(rx_desc->rxdw0); - if (__predict_false(rxdw0 & (R92C_RXDW0_CRCERR | R92C_RXDW0_ICVERR))) { + if (__predict_false(rxdw0 & (RTWN_RXDW0_CRCERR | RTWN_RXDW0_ICVERR))) { /* * This should not happen since we setup our Rx filter * to not receive these frames. */ RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, "%s: RX flags error (%s)\n", __func__, - rxdw0 & R92C_RXDW0_CRCERR ? "CRC" : "ICV"); + rxdw0 & RTWN_RXDW0_CRCERR ? "CRC" : "ICV"); goto fail; } - pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN); + pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN); if (__predict_false(pktlen < sizeof(struct ieee80211_frame_ack) || pktlen > MJUMPAGESIZE)) { RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, @@ -128,8 +126,8 @@ rtwn_pci_rx_frame(struct rtwn_softc *sc, struct r92ce_rx_stat *rx_desc, goto fail; } - infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8; - shift = MS(rxdw0, R92C_RXDW0_SHIFT); + infosz = MS(rxdw0, RTWN_RXDW0_INFOSZ) * 8; + shift = MS(rxdw0, RTWN_RXDW0_SHIFT); m1 = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); if (__predict_false(m1 == NULL)) { @@ -270,9 +268,9 @@ rtwn_pci_rx_done(struct rtwn_softc *sc) bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTREAD); for (;;) { - struct r92ce_rx_stat *rx_desc = &ring->desc[ring->cur]; + struct rtwn_rx_stat_pci *rx_desc = &ring->desc[ring->cur]; - if (le32toh(rx_desc->rxdw0) & R92C_RXDW0_OWN) + if (le32toh(rx_desc->rxdw0) & RTWN_RXDW0_OWN) break; rtwn_pci_rx_frame(sc, rx_desc, ring->cur); diff --git a/freebsd/sys/dev/rtwn/pci/rtwn_pci_rx.h b/freebsd/sys/dev/rtwn/pci/rtwn_pci_rx.h index 265d32d8..30dd785a 100644 --- a/freebsd/sys/dev/rtwn/pci/rtwn_pci_rx.h +++ b/freebsd/sys/dev/rtwn/pci/rtwn_pci_rx.h @@ -21,7 +21,7 @@ void rtwn_pci_dma_map_addr(void *, bus_dma_segment_t *, int, int); void rtwn_pci_setup_rx_desc(struct rtwn_pci_softc *, - struct r92ce_rx_stat *, bus_addr_t, size_t, int); + struct rtwn_rx_stat_pci *, bus_addr_t, size_t, int); void rtwn_pci_intr(void *); #endif /* RTWN_PCI_RX_H */ diff --git a/freebsd/sys/dev/rtwn/pci/rtwn_pci_var.h b/freebsd/sys/dev/rtwn/pci/rtwn_pci_var.h index 5a9e64e7..194fab4a 100644 --- a/freebsd/sys/dev/rtwn/pci/rtwn_pci_var.h +++ b/freebsd/sys/dev/rtwn/pci/rtwn_pci_var.h @@ -23,9 +23,6 @@ #ifndef RTWN_PCI_VAR_H #define RTWN_PCI_VAR_H -#include <dev/rtwn/rtl8192c/pci/r92ce_rx_desc.h> - - #define RTWN_PCI_RX_LIST_COUNT 256 #define RTWN_PCI_TX_LIST_COUNT 256 @@ -36,7 +33,7 @@ struct rtwn_rx_data { }; struct rtwn_rx_ring { - struct r92ce_rx_stat *desc; + struct rtwn_rx_stat_pci *desc; bus_addr_t paddr; bus_dma_tag_t desc_dmat; bus_dmamap_t desc_map; diff --git a/freebsd/sys/dev/rtwn/rtl8188e/r88e_chan.c b/freebsd/sys/dev/rtwn/rtl8188e/r88e_chan.c index fe9d58b7..903398a4 100644 --- a/freebsd/sys/dev/rtwn/rtl8188e/r88e_chan.c +++ b/freebsd/sys/dev/rtwn/rtl8188e/r88e_chan.c @@ -91,8 +91,7 @@ r88e_get_txpower(struct rtwn_softc *sc, int chain, { struct r92c_softc *rs = sc->sc_priv; const struct rtwn_r88e_txpwr *rt = rs->rs_txpwr; - const struct rtwn_r88e_txagc *base = rs->rs_txagc; - uint16_t cckpow, ofdmpow, bw20pow, htpow; + uint8_t cckpow, ofdmpow, bw20pow, htpow = 0; int max_mcs, ridx, group; /* Determine channel group. */ @@ -108,35 +107,24 @@ r88e_get_txpower(struct rtwn_softc *sc, int chain, KASSERT(max_mcs <= RTWN_RIDX_COUNT, ("increase ridx limit\n")); memset(power, 0, max_mcs * sizeof(power[0])); - if (rs->regulatory == 0) { - for (ridx = RTWN_RIDX_CCK1; ridx <= RTWN_RIDX_CCK11; ridx++) - power[ridx] = base->pwr[0][ridx]; - } - for (ridx = RTWN_RIDX_OFDM6; ridx <= max_mcs; ridx++) { - if (rs->regulatory == 3) - power[ridx] = base->pwr[0][ridx]; - else if (rs->regulatory == 1) { - if (!IEEE80211_IS_CHAN_HT40(c)) - power[ridx] = base->pwr[group][ridx]; - } else if (rs->regulatory != 2) - power[ridx] = base->pwr[0][ridx]; - } /* Compute per-CCK rate Tx power. */ cckpow = rt->cck_tx_pwr[group]; - for (ridx = RTWN_RIDX_CCK1; ridx <= RTWN_RIDX_CCK11; ridx++) - power[ridx] += cckpow; + for (ridx = RTWN_RIDX_CCK1; ridx <= RTWN_RIDX_CCK11; ridx++) { + power[ridx] = (ridx == RTWN_RIDX_CCK2) ? cckpow - 9 : cckpow; + } - htpow = rt->ht40_tx_pwr[group]; + if (group < 5) + htpow = rt->ht40_tx_pwr[group]; /* Compute per-OFDM rate Tx power. */ ofdmpow = htpow + rt->ofdm_tx_pwr_diff; for (ridx = RTWN_RIDX_OFDM6; ridx <= RTWN_RIDX_OFDM54; ridx++) - power[ridx] += ofdmpow; + power[ridx] = ofdmpow; bw20pow = htpow + rt->bw20_tx_pwr_diff; for (ridx = RTWN_RIDX_MCS(0); ridx <= max_mcs; ridx++) - power[ridx] += bw20pow; + power[ridx] = bw20pow; /* Apply max limit. */ for (ridx = RTWN_RIDX_CCK1; ridx <= max_mcs; ridx++) { diff --git a/freebsd/sys/dev/rtwn/rtl8188e/r88e_priv.h b/freebsd/sys/dev/rtwn/rtl8188e/r88e_priv.h index cb4f7edb..28f4b1fb 100644 --- a/freebsd/sys/dev/rtwn/rtl8188e/r88e_priv.h +++ b/freebsd/sys/dev/rtwn/rtl8188e/r88e_priv.h @@ -227,47 +227,4 @@ static const struct rtwn_rf_prog rtl8188eu_rf[] = { { 0, NULL, NULL, { 0 }, NULL } }; - -struct rtwn_r88e_txagc { - uint8_t pwr[R88E_GROUP_2G][20]; /* RTWN_RIDX_MCS(7) + 1 */ -}; - -/* - * Per RF chain/group/rate Tx gain values. - */ -static const struct rtwn_r88e_txagc r88e_txagc[] = { - { { /* Chain 0. */ - { /* Group 0. */ - 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ - }, - { /* Group 1. */ - 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ - }, - { /* Group 2. */ - 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ - }, - { /* Group 3. */ - 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ - }, - { /* Group 4. */ - 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ - }, - { /* Group 5. */ - 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ - } - } } -}; - #endif /* R88E_PRIV_H */ diff --git a/freebsd/sys/dev/rtwn/rtl8188e/r88e_rom_defs.h b/freebsd/sys/dev/rtwn/rtl8188e/r88e_rom_defs.h index 5734246f..c6033678 100644 --- a/freebsd/sys/dev/rtwn/rtl8188e/r88e_rom_defs.h +++ b/freebsd/sys/dev/rtwn/rtl8188e/r88e_rom_defs.h @@ -23,7 +23,7 @@ #define R88E_GROUP_2G 6 -#define R88E_EFUSE_MAX_LEN 512 +#define R88E_EFUSE_MAX_LEN 256 #define R88E_EFUSE_MAP_LEN 512 #endif /* R88E_ROM_DEFS_H */ diff --git a/freebsd/sys/dev/rtwn/rtl8188e/r88e_rx.c b/freebsd/sys/dev/rtwn/rtl8188e/r88e_rx.c index 88159876..856ec88b 100644 --- a/freebsd/sys/dev/rtwn/rtl8188e/r88e_rx.c +++ b/freebsd/sys/dev/rtwn/rtl8188e/r88e_rx.c @@ -223,7 +223,7 @@ r88e_get_rx_stats(struct rtwn_softc *sc, struct ieee80211_rx_stats *rxs, if (!sc->sc_ht40) { /* XXX center channel */ rxs->r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ; - rxs->c_ieee = le16toh(physt->chan); + rxs->c_ieee = physt->chan; rxs->c_freq = ieee80211_ieee2mhz(rxs->c_ieee, IEEE80211_CHAN_2GHZ); } diff --git a/freebsd/sys/dev/rtwn/rtl8188e/usb/r88eu_attach.c b/freebsd/sys/dev/rtwn/rtl8188e/usb/r88eu_attach.c index f5ac1d9d..73cc7856 100644 --- a/freebsd/sys/dev/rtwn/rtl8188e/usb/r88eu_attach.c +++ b/freebsd/sys/dev/rtwn/rtl8188e/usb/r88eu_attach.c @@ -87,7 +87,7 @@ r88eu_attach_private(struct rtwn_softc *sc) rs = malloc(sizeof(struct r92c_softc), M_RTWN_PRIV, M_WAITOK | M_ZERO); rs->rs_txpwr = &r88e_txpwr; - rs->rs_txagc = &r88e_txagc; + rs->rs_txagc = NULL; rs->rs_set_bw20 = r88e_set_bw20; rs->rs_get_txpower = r88e_get_txpower; diff --git a/freebsd/sys/dev/rtwn/rtl8192c/r92c_rom_image.h b/freebsd/sys/dev/rtwn/rtl8192c/r92c_rom_image.h index 304324e6..5c2880bb 100644 --- a/freebsd/sys/dev/rtwn/rtl8192c/r92c_rom_image.h +++ b/freebsd/sys/dev/rtwn/rtl8192c/r92c_rom_image.h @@ -48,7 +48,7 @@ struct r92c_rom { uint8_t ofdm_tx_pwr_diff[R92C_GROUP_2G]; uint8_t ht40_max_pwr[R92C_GROUP_2G]; uint8_t ht20_max_pwr[R92C_GROUP_2G]; - uint8_t xtal_calib; + uint8_t channel_plan; uint8_t tssi[R92C_MAX_CHAINS]; uint8_t thermal_meter; #define R92C_ROM_THERMAL_METER_M 0x1f @@ -58,9 +58,7 @@ struct r92c_rom { uint8_t rf_opt2; uint8_t rf_opt3; uint8_t rf_opt4; - uint8_t channel_plan; -#define R92C_CHANNEL_PLAN_BY_HW 0x80 - + uint8_t reserved5; uint8_t version; uint8_t customer_id; } __packed; diff --git a/freebsd/sys/dev/rtwn/rtl8192c/r92c_tx.c b/freebsd/sys/dev/rtwn/rtl8192c/r92c_tx.c index 454da87c..c2a6eab0 100644 --- a/freebsd/sys/dev/rtwn/rtl8192c/r92c_tx.c +++ b/freebsd/sys/dev/rtwn/rtl8192c/r92c_tx.c @@ -333,8 +333,6 @@ r92c_fill_tx_desc(struct rtwn_softc *sc, struct ieee80211_node *ni, if (m->m_flags & M_AMPDU_MPDU) { seqno = ni->ni_txseqs[tid]; - /* NB: clear Fragment Number field. */ - *(uint16_t *)wh->i_seq = 0; ni->ni_txseqs[tid]++; } else seqno = M_SEQNO_GET(m) % IEEE80211_SEQ_RANGE; diff --git a/freebsd/sys/dev/rtwn/rtl8812a/r12a_tx.c b/freebsd/sys/dev/rtwn/rtl8812a/r12a_tx.c index c5a9e465..895f71e4 100644 --- a/freebsd/sys/dev/rtwn/rtl8812a/r12a_tx.c +++ b/freebsd/sys/dev/rtwn/rtl8812a/r12a_tx.c @@ -340,8 +340,6 @@ r12a_fill_tx_desc(struct rtwn_softc *sc, struct ieee80211_node *ni, if (m->m_flags & M_AMPDU_MPDU) { seqno = ni->ni_txseqs[tid]; - /* NB: clear Fragment Number field. */ - *(uint16_t *)wh->i_seq = 0; ni->ni_txseqs[tid]++; } else seqno = M_SEQNO_GET(m) % IEEE80211_SEQ_RANGE; diff --git a/freebsd/sys/dev/rtwn/usb/rtwn_usb_attach.c b/freebsd/sys/dev/rtwn/usb/rtwn_usb_attach.c index 93e1f768..8626d0a3 100644 --- a/freebsd/sys/dev/rtwn/usb/rtwn_usb_attach.c +++ b/freebsd/sys/dev/rtwn/usb/rtwn_usb_attach.c @@ -79,12 +79,14 @@ static void rtwn_usb_reset_lists(struct rtwn_softc *, struct ieee80211vap *); static void rtwn_usb_reset_tx_list(struct rtwn_usb_softc *, rtwn_datahead *, struct ieee80211vap *); +static void rtwn_usb_reset_rx_list(struct rtwn_usb_softc *); static void rtwn_usb_start_xfers(struct rtwn_softc *); static void rtwn_usb_abort_xfers(struct rtwn_softc *); static int rtwn_usb_fw_write_block(struct rtwn_softc *, const uint8_t *, uint16_t, int); static void rtwn_usb_drop_incorrect_tx(struct rtwn_softc *); static void rtwn_usb_attach_methods(struct rtwn_softc *); +static void rtwn_usb_sysctlattach(struct rtwn_softc *); #define RTWN_CONFIG_INDEX 0 @@ -135,9 +137,8 @@ rtwn_usb_alloc_rx_list(struct rtwn_softc *sc) struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc); int error, i; - /* XXX recheck */ error = rtwn_usb_alloc_list(sc, uc->uc_rx, RTWN_USB_RX_LIST_COUNT, - sc->rx_dma_size + 1024); + uc->uc_rx_buf_size * RTWN_USB_RXBUFSZ_UNIT); if (error != 0) return (error); @@ -157,7 +158,7 @@ rtwn_usb_alloc_tx_list(struct rtwn_softc *sc) int error, i; error = rtwn_usb_alloc_list(sc, uc->uc_tx, RTWN_USB_TX_LIST_COUNT, - RTWN_TXBUFSZ); + RTWN_USB_TXBUFSZ); if (error != 0) return (error); @@ -201,6 +202,9 @@ rtwn_usb_free_rx_list(struct rtwn_softc *sc) rtwn_usb_free_list(sc, uc->uc_rx, RTWN_USB_RX_LIST_COUNT); + uc->uc_rx_stat_len = 0; + uc->uc_rx_off = 0; + STAILQ_INIT(&uc->uc_rx_active); STAILQ_INIT(&uc->uc_rx_inactive); } @@ -226,8 +230,10 @@ rtwn_usb_reset_lists(struct rtwn_softc *sc, struct ieee80211vap *vap) rtwn_usb_reset_tx_list(uc, &uc->uc_tx_active, vap); rtwn_usb_reset_tx_list(uc, &uc->uc_tx_pending, vap); - if (vap == NULL) + if (vap == NULL) { + rtwn_usb_reset_rx_list(uc); sc->qfullmsk = 0; + } } static void @@ -261,6 +267,23 @@ rtwn_usb_reset_tx_list(struct rtwn_usb_softc *uc, } static void +rtwn_usb_reset_rx_list(struct rtwn_usb_softc *uc) +{ + int i; + + for (i = 0; i < RTWN_USB_RX_LIST_COUNT; i++) { + struct rtwn_data *dp = &uc->uc_rx[i]; + + if (dp->m != NULL) { + m_freem(dp->m); + dp->m = NULL; + } + } + uc->uc_rx_stat_len = 0; + uc->uc_rx_off = 0; +} + +static void rtwn_usb_start_xfers(struct rtwn_softc *sc) { struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc); @@ -329,6 +352,31 @@ rtwn_usb_attach_methods(struct rtwn_softc *sc) sc->bcn_check_interval = 100; } +static void +rtwn_usb_sysctlattach(struct rtwn_softc *sc) +{ + struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc); + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); + struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); + char str[64]; + int ret; + + ret = snprintf(str, sizeof(str), + "Rx buffer size, 512-byte units [%d...%d]", + RTWN_USB_RXBUFSZ_MIN, RTWN_USB_RXBUFSZ_MAX); + KASSERT(ret > 0, ("ret (%d) <= 0!\n", ret)); + (void) ret; + + uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_DEF; + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "rx_buf_size", CTLFLAG_RDTUN, &uc->uc_rx_buf_size, + uc->uc_rx_buf_size, str); + if (uc->uc_rx_buf_size < RTWN_USB_RXBUFSZ_MIN) + uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_MIN; + if (uc->uc_rx_buf_size > RTWN_USB_RXBUFSZ_MAX) + uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_MAX; +} + static int rtwn_usb_attach(device_t self) { @@ -345,6 +393,7 @@ rtwn_usb_attach(device_t self) /* Need to be initialized early. */ rtwn_sysctlattach(sc); + rtwn_usb_sysctlattach(sc); mtx_init(&sc->sc_mtx, ic->ic_name, MTX_NETWORK_LOCK, MTX_DEF); rtwn_usb_attach_methods(sc); diff --git a/freebsd/sys/dev/rtwn/usb/rtwn_usb_ep.c b/freebsd/sys/dev/rtwn/usb/rtwn_usb_ep.c index ef7d1ffc..f56e96c0 100644 --- a/freebsd/sys/dev/rtwn/usb/rtwn_usb_ep.c +++ b/freebsd/sys/dev/rtwn/usb/rtwn_usb_ep.c @@ -75,7 +75,7 @@ static const struct usb_config rtwn_config_common[RTWN_N_TRANSFER] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, - .bufsize = RTWN_TXBUFSZ, + .bufsize = RTWN_USB_TXBUFSZ, .flags = { .ext_buffer = 1, .pipe_bof = 1, @@ -88,7 +88,7 @@ static const struct usb_config rtwn_config_common[RTWN_N_TRANSFER] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, - .bufsize = RTWN_TXBUFSZ, + .bufsize = RTWN_USB_TXBUFSZ, .flags = { .ext_buffer = 1, .pipe_bof = 1, @@ -101,7 +101,7 @@ static const struct usb_config rtwn_config_common[RTWN_N_TRANSFER] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, - .bufsize = RTWN_TXBUFSZ, + .bufsize = RTWN_USB_TXBUFSZ, .flags = { .ext_buffer = 1, .pipe_bof = 1, @@ -114,7 +114,7 @@ static const struct usb_config rtwn_config_common[RTWN_N_TRANSFER] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, - .bufsize = RTWN_TXBUFSZ, + .bufsize = RTWN_USB_TXBUFSZ, .flags = { .ext_buffer = 1, .pipe_bof = 1, @@ -227,7 +227,8 @@ rtwn_usb_setup_endpoints(struct rtwn_usb_softc *uc) break; } - rtwn_config[RTWN_BULK_RX].bufsize = sc->rx_dma_size + 1024; + rtwn_config[RTWN_BULK_RX].bufsize = + uc->uc_rx_buf_size * RTWN_USB_RXBUFSZ_UNIT; error = usbd_transfer_setup(uc->uc_udev, &iface_index, uc->uc_xfer, rtwn_config, RTWN_N_TRANSFER, uc, &sc->sc_mtx); free(rtwn_config, M_TEMP); diff --git a/freebsd/sys/dev/rtwn/usb/rtwn_usb_rx.c b/freebsd/sys/dev/rtwn/usb/rtwn_usb_rx.c index 4f39b580..9de8fca4 100644 --- a/freebsd/sys/dev/rtwn/usb/rtwn_usb_rx.c +++ b/freebsd/sys/dev/rtwn/usb/rtwn_usb_rx.c @@ -65,57 +65,69 @@ __FBSDID("$FreeBSD$"); #include <dev/rtwn/usb/rtwn_usb_var.h> #include <dev/rtwn/usb/rtwn_usb_rx.h> -#include <dev/rtwn/rtl8192c/r92c_reg.h> /* for CAM_ALGO_NONE */ -#include <dev/rtwn/rtl8192c/r92c_rx_desc.h> +static struct mbuf * rtwn_rxeof(struct rtwn_softc *, struct rtwn_data *, + uint8_t *, int); - -static struct mbuf * -rtwn_rx_copy_to_mbuf(struct rtwn_softc *sc, struct r92c_rx_stat *stat, - int totlen) +static int +rtwn_rx_check_pre_alloc(struct rtwn_softc *sc, + struct rtwn_rx_stat_common *stat) { - struct ieee80211com *ic = &sc->sc_ic; - struct mbuf *m; uint32_t rxdw0; int pktlen; RTWN_ASSERT_LOCKED(sc); - /* Dump Rx descriptor. */ - RTWN_DPRINTF(sc, RTWN_DEBUG_RECV_DESC, - "%s: dw: 0 %08X, 1 %08X, 2 %08X, 3 %08X, 4 %08X, tsfl %08X\n", - __func__, le32toh(stat->rxdw0), le32toh(stat->rxdw1), - le32toh(stat->rxdw2), le32toh(stat->rxdw3), le32toh(stat->rxdw4), - le32toh(stat->tsf_low)); - /* * don't pass packets to the ieee80211 framework if the driver isn't * RUNNING. */ if (!(sc->sc_flags & RTWN_RUNNING)) - return (NULL); + return (-1); rxdw0 = le32toh(stat->rxdw0); - if (__predict_false(rxdw0 & (R92C_RXDW0_CRCERR | R92C_RXDW0_ICVERR))) { + if (__predict_false(rxdw0 & (RTWN_RXDW0_CRCERR | RTWN_RXDW0_ICVERR))) { /* * This should not happen since we setup our Rx filter * to not receive these frames. */ RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, "%s: RX flags error (%s)\n", __func__, - rxdw0 & R92C_RXDW0_CRCERR ? "CRC" : "ICV"); - goto fail; + rxdw0 & RTWN_RXDW0_CRCERR ? "CRC" : "ICV"); + return (-1); } - pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN); + pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN); if (__predict_false(pktlen < sizeof(struct ieee80211_frame_ack))) { /* * Should not happen (because of Rx filter setup). */ RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, "%s: frame is too short: %d\n", __func__, pktlen); - goto fail; + return (-1); } + return (0); +} + +static struct mbuf * +rtwn_rx_copy_to_mbuf(struct rtwn_softc *sc, struct rtwn_rx_stat_common *stat, + int totlen) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct mbuf *m; + + RTWN_ASSERT_LOCKED(sc); + + /* Dump Rx descriptor. */ + RTWN_DPRINTF(sc, RTWN_DEBUG_RECV_DESC, + "%s: dw: 0 %08X, 1 %08X, 2 %08X, 3 %08X, 4 %08X, tsfl %08X\n", + __func__, le32toh(stat->rxdw0), le32toh(stat->rxdw1), + le32toh(stat->rxdw2), le32toh(stat->rxdw3), le32toh(stat->rxdw4), + le32toh(stat->tsf_low)); + + if (rtwn_rx_check_pre_alloc(sc, stat) != 0) + goto fail; + m = m_get2(totlen, M_NOWAIT, MT_DATA, M_PKTHDR); if (__predict_false(m == NULL)) { device_printf(sc->sc_dev, "%s: could not allocate RX mbuf\n", @@ -139,30 +151,124 @@ fail: } static struct mbuf * -rtwn_rxeof(struct rtwn_softc *sc, uint8_t *buf, int len) +rtwn_rxeof_fragmented(struct rtwn_usb_softc *uc, struct rtwn_data *data, + uint8_t *buf, int len) +{ + struct rtwn_softc *sc = &uc->uc_sc; + struct ieee80211com *ic = &sc->sc_ic; + struct rtwn_rx_stat_common *stat = &uc->uc_rx_stat; + uint32_t rxdw0; + int totlen, pktlen, infosz, min_len; + int orig_len = len; + int alloc_mbuf = 0; + + /* Check if Rx descriptor is not truncated. */ + if (uc->uc_rx_stat_len < sizeof(*stat)) { + min_len = min(sizeof(*stat) - uc->uc_rx_stat_len, len); + memcpy((uint8_t *)stat + uc->uc_rx_stat_len, buf, min_len); + + uc->uc_rx_stat_len += min_len; + buf += min_len; + len -= min_len; + + if (uc->uc_rx_stat_len < sizeof(*stat)) + goto end; + + KASSERT(data->m == NULL, ("%s: data->m != NULL!\n", __func__)); + alloc_mbuf = 1; + + /* Dump Rx descriptor. */ + RTWN_DPRINTF(sc, RTWN_DEBUG_RECV_DESC, + "%s: dw: 0 %08X, 1 %08X, 2 %08X, 3 %08X, 4 %08X, " + "tsfl %08X\n", __func__, le32toh(stat->rxdw0), + le32toh(stat->rxdw1), le32toh(stat->rxdw2), + le32toh(stat->rxdw3), le32toh(stat->rxdw4), + le32toh(stat->tsf_low)); + } + + rxdw0 = le32toh(stat->rxdw0); + pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN); + infosz = MS(rxdw0, RTWN_RXDW0_INFOSZ) * 8; + totlen = sizeof(*stat) + infosz + pktlen; + if (alloc_mbuf) { + if (rtwn_rx_check_pre_alloc(sc, stat) == 0) { + data->m = m_getm(NULL, totlen, M_NOWAIT, MT_DATA); + if (data->m != NULL) { + m_copyback(data->m, 0, uc->uc_rx_stat_len, + (caddr_t)stat); + + if (rtwn_check_frame(sc, data->m) != 0) { + m_freem(data->m); + data->m = NULL; + counter_u64_add(ic->ic_ierrors, 1); + } + } else + counter_u64_add(ic->ic_ierrors, 1); + } else + counter_u64_add(ic->ic_ierrors, 1); + + uc->uc_rx_off = sizeof(*stat); + } + + /* If mbuf allocation fails just discard the data. */ + min_len = min(totlen - uc->uc_rx_off, len); + if (data->m != NULL) + m_copyback(data->m, uc->uc_rx_off, min_len, buf); + + uc->uc_rx_off += min_len; + if (uc->uc_rx_off == totlen) { + /* Align next frame. */ + min_len = rtwn_usb_align_rx(uc, + orig_len - len + min_len, orig_len); + min_len -= (orig_len - len); + KASSERT(len >= min_len, ("%s: len (%d) < min_len (%d)!\n", + __func__, len, min_len)); + + /* Clear mbuf stats. */ + uc->uc_rx_stat_len = 0; + uc->uc_rx_off = 0; + } + len -= min_len; + buf += min_len; +end: + if (uc->uc_rx_stat_len == 0) + return (rtwn_rxeof(sc, data, buf, len)); + else + return (NULL); +} + +static struct mbuf * +rtwn_rxeof(struct rtwn_softc *sc, struct rtwn_data *data, uint8_t *buf, + int len) { struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc); - struct r92c_rx_stat *stat; + struct rtwn_rx_stat_common *stat; struct mbuf *m, *m0 = NULL; uint32_t rxdw0; int totlen, pktlen, infosz; + /* Prepend defragmented frame (if any). */ + if (data->m != NULL) { + m0 = m = data->m; + data->m = NULL; + } + /* Process packets. */ while (len >= sizeof(*stat)) { - stat = (struct r92c_rx_stat *)buf; + stat = (struct rtwn_rx_stat_common *)buf; rxdw0 = le32toh(stat->rxdw0); - pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN); + pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN); if (__predict_false(pktlen == 0)) break; - infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8; + infosz = MS(rxdw0, RTWN_RXDW0_INFOSZ) * 8; /* Make sure everything fits in xfer. */ totlen = sizeof(*stat) + infosz + pktlen; if (totlen > len) { - device_printf(sc->sc_dev, - "%s: totlen (%d) > len (%d)!\n", + RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, + "%s: frame is fragmented (totlen %d len %d)\n", __func__, totlen, len); break; } @@ -170,9 +276,9 @@ rtwn_rxeof(struct rtwn_softc *sc, uint8_t *buf, int len) if (m0 == NULL) m0 = m = rtwn_rx_copy_to_mbuf(sc, stat, totlen); else { - m->m_next = rtwn_rx_copy_to_mbuf(sc, stat, totlen); - if (m->m_next != NULL) - m = m->m_next; + m->m_nextpkt = rtwn_rx_copy_to_mbuf(sc, stat, totlen); + if (m->m_nextpkt != NULL) + m = m->m_nextpkt; } /* Align next frame. */ @@ -181,6 +287,9 @@ rtwn_rxeof(struct rtwn_softc *sc, uint8_t *buf, int len) len -= totlen; } + if (len > 0) + (void)rtwn_rxeof_fragmented(uc, data, buf, len); + return (m0); } @@ -195,15 +304,19 @@ rtwn_report_intr(struct rtwn_usb_softc *uc, struct usb_xfer *xfer, usbd_xfer_status(xfer, &len, NULL, NULL, NULL); - if (__predict_false(len < sizeof(struct r92c_rx_stat))) { + if (__predict_false(len < sizeof(struct rtwn_rx_stat_common) && + uc->uc_rx_stat_len == 0)) { counter_u64_add(ic->ic_ierrors, 1); return (NULL); } buf = data->buf; + if (uc->uc_rx_stat_len > 0) + return (rtwn_rxeof_fragmented(uc, data, data->buf, len)); + switch (rtwn_classify_intr(sc, buf, len)) { case RTWN_RX_DATA: - return (rtwn_rxeof(sc, buf, len)); + return (rtwn_rxeof(sc, data, buf, len)); case RTWN_RX_TX_REPORT: if (sc->sc_ratectl != RTWN_RATECTL_NET80211) { /* shouldn't happen */ @@ -240,11 +353,11 @@ rtwn_report_intr(struct rtwn_usb_softc *uc, struct usb_xfer *xfer, static struct ieee80211_node * rtwn_rx_frame(struct rtwn_softc *sc, struct mbuf *m) { - struct r92c_rx_stat stat; + struct rtwn_rx_stat_common stat; /* Imitate PCIe layout. */ - m_copydata(m, 0, sizeof(struct r92c_rx_stat), (caddr_t)&stat); - m_adj(m, sizeof(struct r92c_rx_stat)); + m_copydata(m, 0, sizeof(stat), (caddr_t)&stat); + m_adj(m, sizeof(stat)); return (rtwn_rx_common(sc, m, &stat)); } @@ -289,8 +402,8 @@ tr_setup: * callback and safe to unlock. */ while (m != NULL) { - next = m->m_next; - m->m_next = NULL; + next = m->m_nextpkt; + m->m_nextpkt = NULL; ni = rtwn_rx_frame(sc, m); @@ -314,6 +427,8 @@ tr_setup: STAILQ_INSERT_TAIL(&uc->uc_rx_inactive, data, next); } if (error != USB_ERR_CANCELLED) { + /* XXX restart device if frame was fragmented? */ + usbd_xfer_set_stall(xfer); counter_u64_add(ic->ic_ierrors, 1); goto tr_setup; diff --git a/freebsd/sys/dev/rtwn/usb/rtwn_usb_tx.c b/freebsd/sys/dev/rtwn/usb/rtwn_usb_tx.c index 7bede4dc..61f0ba43 100644 --- a/freebsd/sys/dev/rtwn/usb/rtwn_usb_tx.c +++ b/freebsd/sys/dev/rtwn/usb/rtwn_usb_tx.c @@ -235,6 +235,9 @@ rtwn_usb_tx_start(struct rtwn_softc *sc, struct ieee80211_node *ni, RTWN_ASSERT_LOCKED(sc); + if (m->m_pkthdr.len + sc->txdesc_len > RTWN_USB_TXBUFSZ) + return (EINVAL); + data = rtwn_usb_getbuf(uc); if (data == NULL) return (ENOBUFS); diff --git a/freebsd/sys/dev/rtwn/usb/rtwn_usb_var.h b/freebsd/sys/dev/rtwn/usb/rtwn_usb_var.h index be7f8f19..7ef21463 100644 --- a/freebsd/sys/dev/rtwn/usb/rtwn_usb_var.h +++ b/freebsd/sys/dev/rtwn/usb/rtwn_usb_var.h @@ -21,6 +21,14 @@ #ifndef RTWN_USBVAR_H #define RTWN_USBVAR_H +#include <dev/rtwn/if_rtwnreg.h> /* for struct rtwn_rx_stat_common */ + +#define RTWN_USB_RXBUFSZ_UNIT (512) +#define RTWN_USB_RXBUFSZ_MIN ( 4) +#define RTWN_USB_RXBUFSZ_DEF (24) +#define RTWN_USB_RXBUFSZ_MAX (64) +#define RTWN_USB_TXBUFSZ (16 * 1024) + #define RTWN_IFACE_INDEX 0 #define RTWN_USB_RX_LIST_COUNT 1 @@ -56,6 +64,12 @@ struct rtwn_usb_softc { struct rtwn_data uc_rx[RTWN_USB_RX_LIST_COUNT]; rtwn_datahead uc_rx_active; rtwn_datahead uc_rx_inactive; + int uc_rx_buf_size; + + struct rtwn_rx_stat_common uc_rx_stat; + int uc_rx_stat_len; + int uc_rx_off; + struct rtwn_data uc_tx[RTWN_USB_TX_LIST_COUNT]; rtwn_datahead uc_tx_active; rtwn_datahead uc_tx_inactive; diff --git a/freebsd/sys/dev/sdhci/sdhci.c b/freebsd/sys/dev/sdhci/sdhci.c index c87199a8..f1616a6e 100644 --- a/freebsd/sys/dev/sdhci/sdhci.c +++ b/freebsd/sys/dev/sdhci/sdhci.c @@ -2,6 +2,7 @@ /*- * Copyright (c) 2008 Alexander Motin <mav@FreeBSD.org> + * Copyright (c) 2017 Marius Strobl <marius@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,7 +35,9 @@ __FBSDID("$FreeBSD$"); #include <sys/callout.h> #include <sys/conf.h> #include <sys/kernel.h> +#include <sys/kobj.h> #include <sys/lock.h> +#include <sys/malloc.h> #include <sys/module.h> #include <sys/mutex.h> #include <rtems/bsd/sys/resource.h> @@ -50,13 +53,22 @@ __FBSDID("$FreeBSD$"); #include <dev/mmc/mmcreg.h> #include <dev/mmc/mmcbrvar.h> +#include <dev/sdhci/sdhci.h> + +#include <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/cam_debug.h> +#include <cam/cam_sim.h> +#include <cam/cam_xpt_sim.h> + #include <rtems/bsd/local/mmcbr_if.h> -#include "sdhci.h" #include <rtems/bsd/local/sdhci_if.h> +#include <rtems/bsd/local/opt_mmccam.h> + SYSCTL_NODE(_hw, OID_AUTO, sdhci, CTLFLAG_RD, 0, "sdhci driver"); -static int sdhci_debug; +static int sdhci_debug = 0; SYSCTL_INT(_hw_sdhci, OID_AUTO, debug, CTLFLAG_RWTUN, &sdhci_debug, 0, "Debug level"); u_int sdhci_quirk_clear = 0; @@ -78,17 +90,30 @@ SYSCTL_INT(_hw_sdhci, OID_AUTO, quirk_set, CTLFLAG_RWTUN, &sdhci_quirk_set, 0, #define WR_MULTI_4(slot, off, ptr, count) \ SDHCI_WRITE_MULTI_4((slot)->bus, (slot), (off), (ptr), (count)) +static void sdhci_card_poll(void *arg); +static void sdhci_card_task(void *arg, int pending); +static int sdhci_exec_tuning(struct sdhci_slot *slot, bool reset); +static void sdhci_req_wakeup(struct mmc_request *req); +static void sdhci_retune(void *arg); static void sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock); static void sdhci_start(struct sdhci_slot *slot); static void sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data); -static void sdhci_card_poll(void *); -static void sdhci_card_task(void *, int); +#ifdef MMCCAM +/* CAM-related */ +int sdhci_cam_get_possible_host_clock(struct sdhci_slot *slot, int proposed_clock); +static int sdhci_cam_update_ios(struct sdhci_slot *slot); +static int sdhci_cam_request(struct sdhci_slot *slot, union ccb *ccb); +static void sdhci_cam_action(struct cam_sim *sim, union ccb *ccb); +static void sdhci_cam_poll(struct cam_sim *sim); +static int sdhci_cam_settran_settings(struct sdhci_slot *slot, union ccb *ccb); +#endif /* helper routines */ static void sdhci_dumpregs(struct sdhci_slot *slot); static int slot_printf(struct sdhci_slot *slot, const char * fmt, ...) __printflike(2, 3); +static uint32_t sdhci_tuning_intmask(struct sdhci_slot *slot); #define SDHCI_LOCK(_slot) mtx_lock(&(_slot)->mtx) #define SDHCI_UNLOCK(_slot) mtx_unlock(&(_slot)->mtx) @@ -169,13 +194,13 @@ sdhci_dumpregs(struct sdhci_slot *slot) RD1(slot, SDHCI_TIMEOUT_CONTROL), RD4(slot, SDHCI_INT_STATUS)); slot_printf(slot, "Int enab: 0x%08x | Sig enab: 0x%08x\n", RD4(slot, SDHCI_INT_ENABLE), RD4(slot, SDHCI_SIGNAL_ENABLE)); - slot_printf(slot, "AC12 err: 0x%08x | Host ctl2: 0x%08x\n", + slot_printf(slot, "AC12 err: 0x%08x | Host ctl2:0x%08x\n", RD2(slot, SDHCI_ACMD12_ERR), RD2(slot, SDHCI_HOST_CONTROL2)); slot_printf(slot, "Caps: 0x%08x | Caps2: 0x%08x\n", RD4(slot, SDHCI_CAPABILITIES), RD4(slot, SDHCI_CAPABILITIES2)); slot_printf(slot, "Max curr: 0x%08x | ADMA err: 0x%08x\n", RD4(slot, SDHCI_MAX_CURRENT), RD1(slot, SDHCI_ADMA_ERR)); - slot_printf(slot, "ADMA addr: 0x%08x | Slot int: 0x%08x\n", + slot_printf(slot, "ADMA addr:0x%08x | Slot int: 0x%08x\n", RD4(slot, SDHCI_ADMA_ADDRESS_LO), RD2(slot, SDHCI_SLOT_INT_STATUS)); slot_printf(slot, @@ -242,6 +267,21 @@ sdhci_reset(struct sdhci_slot *slot, uint8_t mask) } } +static uint32_t +sdhci_tuning_intmask(struct sdhci_slot *slot) +{ + uint32_t intmask; + + intmask = 0; + if (slot->opt & SDHCI_TUNING_SUPPORTED) { + intmask |= SDHCI_INT_TUNEERR; + if (slot->retune_mode == SDHCI_RETUNE_MODE_2 || + slot->retune_mode == SDHCI_RETUNE_MODE_3) + intmask |= SDHCI_INT_RETUNE; + } + return (intmask); +} + static void sdhci_init(struct sdhci_slot *slot) { @@ -261,7 +301,7 @@ sdhci_init(struct sdhci_slot *slot) slot->intmask |= SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT; } - WR4(slot, SDHCI_INT_ENABLE, slot->intmask); + WR4(slot, SDHCI_INT_ENABLE, slot->intmask | sdhci_tuning_intmask(slot)); WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask); } @@ -368,6 +408,7 @@ sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock) static void sdhci_set_power(struct sdhci_slot *slot, u_char power) { + int i; uint8_t pwr; if (slot->power == power) @@ -396,9 +437,20 @@ sdhci_set_power(struct sdhci_slot *slot, u_char power) break; } WR1(slot, SDHCI_POWER_CONTROL, pwr); - /* Turn on the power. */ + /* + * Turn on VDD1 power. Note that at least some Intel controllers can + * fail to enable bus power on the first try after transiting from D3 + * to D0, so we give them up to 2 ms. + */ pwr |= SDHCI_POWER_ON; - WR1(slot, SDHCI_POWER_CONTROL, pwr); + for (i = 0; i < 20; i++) { + WR1(slot, SDHCI_POWER_CONTROL, pwr); + if (RD1(slot, SDHCI_POWER_CONTROL) & SDHCI_POWER_ON) + break; + DELAY(100); + } + if (!(RD1(slot, SDHCI_POWER_CONTROL) & SDHCI_POWER_ON)) + slot_printf(slot, "Bus power failed to enable"); if (slot->quirks & SDHCI_QUIRK_INTEL_POWER_UP_RESET) { WR1(slot, SDHCI_POWER_CONTROL, pwr | 0x10); @@ -521,25 +573,93 @@ sdhci_card_task(void *arg, int pending __unused) SDHCI_LOCK(slot); if (SDHCI_GET_CARD_PRESENT(slot->bus, slot)) { +#ifdef MMCCAM + if (slot->card_present == 0) { +#else if (slot->dev == NULL) { +#endif /* If card is present - attach mmc bus. */ if (bootverbose || sdhci_debug) slot_printf(slot, "Card inserted\n"); - slot->dev = device_add_child(slot->bus, "mmc", -1); - device_set_ivars(slot->dev, slot); +#ifdef MMCCAM + slot->card_present = 1; + union ccb *ccb; + uint32_t pathid; + pathid = cam_sim_path(slot->sim); + ccb = xpt_alloc_ccb_nowait(); + if (ccb == NULL) { + slot_printf(slot, "Unable to alloc CCB for rescan\n"); + SDHCI_UNLOCK(slot); + return; + } + + /* + * We create a rescan request for BUS:0:0, since the card + * will be at lun 0. + */ + if (xpt_create_path(&ccb->ccb_h.path, NULL, pathid, + /* target */ 0, /* lun */ 0) != CAM_REQ_CMP) { + slot_printf(slot, "Unable to create path for rescan\n"); + SDHCI_UNLOCK(slot); + xpt_free_ccb(ccb); + return; + } + SDHCI_UNLOCK(slot); + xpt_rescan(ccb); +#else + d = slot->dev = device_add_child(slot->bus, "mmc", -1); SDHCI_UNLOCK(slot); - device_probe_and_attach(slot->dev); + if (d) { + device_set_ivars(d, slot); + (void)device_probe_and_attach(d); + } +#endif } else SDHCI_UNLOCK(slot); } else { +#ifdef MMCCAM + if (slot->card_present == 1) { +#else if (slot->dev != NULL) { +#endif /* If no card present - detach mmc bus. */ if (bootverbose || sdhci_debug) slot_printf(slot, "Card removed\n"); d = slot->dev; slot->dev = NULL; +#ifdef MMCCAM + slot->card_present = 0; + union ccb *ccb; + uint32_t pathid; + pathid = cam_sim_path(slot->sim); + ccb = xpt_alloc_ccb_nowait(); + if (ccb == NULL) { + slot_printf(slot, "Unable to alloc CCB for rescan\n"); + SDHCI_UNLOCK(slot); + return; + } + + /* + * We create a rescan request for BUS:0:0, since the card + * will be at lun 0. + */ + if (xpt_create_path(&ccb->ccb_h.path, NULL, pathid, + /* target */ 0, /* lun */ 0) != CAM_REQ_CMP) { + slot_printf(slot, "Unable to create path for rescan\n"); + SDHCI_UNLOCK(slot); + xpt_free_ccb(ccb); + return; + } SDHCI_UNLOCK(slot); + xpt_rescan(ccb); +#else + slot->intmask &= ~sdhci_tuning_intmask(slot); + WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask); + slot->opt &= ~SDHCI_TUNING_ENABLED; + SDHCI_UNLOCK(slot); + callout_drain(&slot->retune_callout); device_delete_child(slot->bus, d); +#endif } else SDHCI_UNLOCK(slot); } @@ -561,7 +681,11 @@ sdhci_handle_card_present_locked(struct sdhci_slot *slot, bool is_present) * because once power is removed, a full card re-init is needed, and * that happens by deleting and recreating the child device. */ +#ifdef MMCCAM + was_present = slot->card_present; +#else was_present = slot->dev != NULL; +#endif if (!was_present && is_present) { taskqueue_enqueue_timeout(taskqueue_swi_giant, &slot->card_delayed_task, -SDHCI_INSERT_DELAY_TICKS); @@ -593,10 +717,13 @@ sdhci_card_poll(void *arg) int sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num) { + kobjop_desc_t kobj_desc; + kobj_method_t *kobj_method; uint32_t caps, caps2, freq, host_caps; int err; SDHCI_LOCK_INIT(slot); + slot->num = num; slot->bus = dev; @@ -617,6 +744,7 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num) BUS_DMA_NOWAIT, &slot->dmamap); if (err != 0) { device_printf(dev, "Can't alloc DMA memory\n"); + bus_dma_tag_destroy(slot->dmatag); SDHCI_LOCK_DESTROY(slot); return (err); } @@ -626,6 +754,8 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num) sdhci_getaddr, &slot->paddr, 0); if (err != 0 || slot->paddr == 0) { device_printf(dev, "Can't load DMA memory\n"); + bus_dmamem_free(slot->dmatag, slot->dmamem, slot->dmamap); + bus_dma_tag_destroy(slot->dmatag); SDHCI_LOCK_DESTROY(slot); if (err) return (err); @@ -633,8 +763,6 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num) return (EFAULT); } - /* Initialize slot. */ - sdhci_init(slot); slot->version = (RD2(slot, SDHCI_HOST_VERSION) >> SDHCI_SPEC_VER_SHIFT) & SDHCI_SPEC_VER_MASK; if (slot->quirks & SDHCI_QUIRK_MISSING_CAPS) { @@ -647,6 +775,22 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num) else caps2 = 0; } + if (slot->version >= SDHCI_SPEC_300) { + if ((caps & SDHCI_SLOTTYPE_MASK) != SDHCI_SLOTTYPE_REMOVABLE && + (caps & SDHCI_SLOTTYPE_MASK) != SDHCI_SLOTTYPE_EMBEDDED) { + device_printf(dev, + "Driver doesn't support shared bus slots\n"); + bus_dmamap_unload(slot->dmatag, slot->dmamap); + bus_dmamem_free(slot->dmatag, slot->dmamem, + slot->dmamap); + bus_dma_tag_destroy(slot->dmatag); + SDHCI_LOCK_DESTROY(slot); + return (ENXIO); + } else if ((caps & SDHCI_SLOTTYPE_MASK) == + SDHCI_SLOTTYPE_EMBEDDED) { + slot->opt |= SDHCI_SLOT_EMBEDDED | SDHCI_NON_REMOVABLE; + } + } /* Calculate base clock frequency. */ if (slot->version >= SDHCI_SPEC_300) freq = (caps & SDHCI_CLOCK_V3_BASE_MASK) >> @@ -696,12 +840,14 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num) slot->host.host_ocr |= MMC_OCR_320_330 | MMC_OCR_330_340; if (caps & SDHCI_CAN_VDD_300) slot->host.host_ocr |= MMC_OCR_290_300 | MMC_OCR_300_310; - if (caps & SDHCI_CAN_VDD_180) + /* 1.8V VDD is not supposed to be used for removable cards. */ + if ((caps & SDHCI_CAN_VDD_180) && (slot->opt & SDHCI_SLOT_EMBEDDED)) slot->host.host_ocr |= MMC_OCR_LOW_VOLTAGE; if (slot->host.host_ocr == 0) { device_printf(dev, "Hardware doesn't report any " "support voltages.\n"); } + host_caps = MMC_CAP_4_BIT_DATA; if (caps & SDHCI_CAN_DO_8BITBUS) host_caps |= MMC_CAP_8_BIT_DATA; @@ -711,6 +857,8 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num) host_caps |= MMC_CAP_BOOT_NOACC; if (slot->quirks & SDHCI_QUIRK_WAIT_WHILE_BUSY) host_caps |= MMC_CAP_WAIT_WHILE_BUSY; + + /* Determine supported UHS-I and eMMC modes. */ if (caps2 & (SDHCI_CAN_SDR50 | SDHCI_CAN_SDR104 | SDHCI_CAN_DDR50)) host_caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25; if (caps2 & SDHCI_CAN_SDR104) { @@ -727,12 +875,91 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num) if (slot->quirks & SDHCI_QUIRK_CAPS_BIT63_FOR_MMC_HS400 && caps2 & SDHCI_CAN_MMC_HS400) host_caps |= MMC_CAP_MMC_HS400; + + /* + * Disable UHS-I and eMMC modes if the set_uhs_timing method is the + * default NULL implementation. + */ + kobj_desc = &sdhci_set_uhs_timing_desc; + kobj_method = kobj_lookup_method(((kobj_t)dev)->ops->cls, NULL, + kobj_desc); + if (kobj_method == &kobj_desc->deflt) + host_caps &= ~(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_DDR50 | MMC_CAP_UHS_SDR104 | + MMC_CAP_MMC_DDR52 | MMC_CAP_MMC_HS200 | MMC_CAP_MMC_HS400); + +#define SDHCI_CAP_MODES_TUNING(caps2) \ + (((caps2) & SDHCI_TUNE_SDR50 ? MMC_CAP_UHS_SDR50 : 0) | \ + MMC_CAP_UHS_DDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_MMC_HS200 | \ + MMC_CAP_MMC_HS400) + + /* + * Disable UHS-I and eMMC modes that require (re-)tuning if either + * the tune or re-tune method is the default NULL implementation. + */ + kobj_desc = &mmcbr_tune_desc; + kobj_method = kobj_lookup_method(((kobj_t)dev)->ops->cls, NULL, + kobj_desc); + if (kobj_method == &kobj_desc->deflt) + goto no_tuning; + kobj_desc = &mmcbr_retune_desc; + kobj_method = kobj_lookup_method(((kobj_t)dev)->ops->cls, NULL, + kobj_desc); + if (kobj_method == &kobj_desc->deflt) { +no_tuning: + host_caps &= ~(SDHCI_CAP_MODES_TUNING(caps2)); + } + + /* Allocate tuning structures and determine tuning parameters. */ + if (host_caps & SDHCI_CAP_MODES_TUNING(caps2)) { + slot->opt |= SDHCI_TUNING_SUPPORTED; + slot->tune_req = malloc(sizeof(*slot->tune_req), M_DEVBUF, + M_WAITOK); + slot->tune_cmd = malloc(sizeof(*slot->tune_cmd), M_DEVBUF, + M_WAITOK); + slot->tune_data = malloc(sizeof(*slot->tune_data), M_DEVBUF, + M_WAITOK); + if (caps2 & SDHCI_TUNE_SDR50) + slot->opt |= SDHCI_SDR50_NEEDS_TUNING; + slot->retune_mode = (caps2 & SDHCI_RETUNE_MODES_MASK) >> + SDHCI_RETUNE_MODES_SHIFT; + if (slot->retune_mode == SDHCI_RETUNE_MODE_1) { + slot->retune_count = (caps2 & SDHCI_RETUNE_CNT_MASK) >> + SDHCI_RETUNE_CNT_SHIFT; + if (slot->retune_count > 0xb) { + device_printf(dev, "Unknown re-tuning count " + "%x, using 1 sec\n", slot->retune_count); + slot->retune_count = 1; + } else if (slot->retune_count != 0) + slot->retune_count = + 1 << (slot->retune_count - 1); + } + } + +#undef SDHCI_CAP_MODES_TUNING + + /* Determine supported VCCQ signaling levels. */ host_caps |= MMC_CAP_SIGNALING_330; if (host_caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | - MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_DDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_MMC_DDR52_180 | MMC_CAP_MMC_HS200_180 | MMC_CAP_MMC_HS400_180)) - host_caps |= MMC_CAP_SIGNALING_180; + host_caps |= MMC_CAP_SIGNALING_120 | MMC_CAP_SIGNALING_180; + + /* + * Disable 1.2 V and 1.8 V signaling if the switch_vccq method is the + * default NULL implementation. Disable 1.2 V support if it's the + * generic SDHCI implementation. + */ + kobj_desc = &mmcbr_switch_vccq_desc; + kobj_method = kobj_lookup_method(((kobj_t)dev)->ops->cls, NULL, + kobj_desc); + if (kobj_method == &kobj_desc->deflt) + host_caps &= ~(MMC_CAP_SIGNALING_120 | MMC_CAP_SIGNALING_180); + else if (kobj_method->func == (kobjop_t)sdhci_generic_switch_vccq) + host_caps &= ~MMC_CAP_SIGNALING_120; + + /* Determine supported driver types (type B is always mandatory). */ if (caps2 & SDHCI_CAN_DRIVE_TYPE_A) host_caps |= MMC_CAP_DRIVER_TYPE_A; if (caps2 & SDHCI_CAN_DRIVE_TYPE_C) @@ -761,20 +988,24 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num) if (bootverbose || sdhci_debug) { slot_printf(slot, - "%uMHz%s %s VDD:%s%s%s VCCQ: 3.3V%s%s DRV: B%s%s%s %s\n", + "%uMHz%s %s VDD:%s%s%s VCCQ: 3.3V%s%s DRV: B%s%s%s %s %s\n", slot->max_clk / 1000000, (caps & SDHCI_CAN_DO_HISPD) ? " HS" : "", (host_caps & MMC_CAP_8_BIT_DATA) ? "8bits" : ((host_caps & MMC_CAP_4_BIT_DATA) ? "4bits" : "1bit"), (caps & SDHCI_CAN_VDD_330) ? " 3.3V" : "", (caps & SDHCI_CAN_VDD_300) ? " 3.0V" : "", - (caps & SDHCI_CAN_VDD_180) ? " 1.8V" : "", + ((caps & SDHCI_CAN_VDD_180) && + (slot->opt & SDHCI_SLOT_EMBEDDED)) ? " 1.8V" : "", (host_caps & MMC_CAP_SIGNALING_180) ? " 1.8V" : "", (host_caps & MMC_CAP_SIGNALING_120) ? " 1.2V" : "", - (caps2 & SDHCI_CAN_DRIVE_TYPE_A) ? "A" : "", - (caps2 & SDHCI_CAN_DRIVE_TYPE_C) ? "C" : "", - (caps2 & SDHCI_CAN_DRIVE_TYPE_D) ? "D" : "", - (slot->opt & SDHCI_HAVE_DMA) ? "DMA" : "PIO"); + (host_caps & MMC_CAP_DRIVER_TYPE_A) ? "A" : "", + (host_caps & MMC_CAP_DRIVER_TYPE_C) ? "C" : "", + (host_caps & MMC_CAP_DRIVER_TYPE_D) ? "D" : "", + (slot->opt & SDHCI_HAVE_DMA) ? "DMA" : "PIO", + (slot->opt & SDHCI_SLOT_EMBEDDED) ? "embedded" : + (slot->opt & SDHCI_NON_REMOVABLE) ? "non-removable" : + "removable"); if (host_caps & (MMC_CAP_MMC_DDR52 | MMC_CAP_MMC_HS200 | MMC_CAP_MMC_HS400 | MMC_CAP_MMC_ENH_STROBE)) slot_printf(slot, "eMMC:%s%s%s%s\n", @@ -793,6 +1024,9 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num) (host_caps & MMC_CAP_UHS_SDR50) ? " SDR50" : "", (host_caps & MMC_CAP_UHS_SDR104) ? " SDR104" : "", (host_caps & MMC_CAP_UHS_DDR50) ? " DDR50" : ""); + if (slot->opt & SDHCI_TUNING_SUPPORTED) + slot_printf(slot, "Re-tuning count %d secs, mode %d\n", + slot->retune_count, slot->retune_mode + 1); sdhci_dumpregs(slot); } @@ -806,6 +1040,7 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num) sdhci_card_task, slot); callout_init(&slot->card_poll_callout, 1); callout_init_mtx(&slot->timeout_callout, &slot->mtx, 0); + callout_init_mtx(&slot->retune_callout, &slot->mtx, 0); if ((slot->quirks & SDHCI_QUIRK_POLL_CARD_PRESENT) && !(slot->opt & SDHCI_NON_REMOVABLE)) { @@ -813,6 +1048,8 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num) SDHCI_CARD_PRESENT_TICKS, sdhci_card_poll, slot); } + sdhci_init(slot); + return (0); } @@ -830,6 +1067,7 @@ sdhci_cleanup_slot(struct sdhci_slot *slot) callout_drain(&slot->timeout_callout); callout_drain(&slot->card_poll_callout); + callout_drain(&slot->retune_callout); taskqueue_drain(taskqueue_swi_giant, &slot->card_task); taskqueue_drain_timeout(taskqueue_swi_giant, &slot->card_delayed_task); @@ -846,6 +1084,11 @@ sdhci_cleanup_slot(struct sdhci_slot *slot) bus_dmamap_unload(slot->dmatag, slot->dmamap); bus_dmamem_free(slot->dmatag, slot->dmamem, slot->dmamap); bus_dma_tag_destroy(slot->dmatag); + if (slot->opt & SDHCI_TUNING_SUPPORTED) { + free(slot->tune_req, M_DEVBUF); + free(slot->tune_cmd, M_DEVBUF); + free(slot->tune_data, M_DEVBUF); + } SDHCI_LOCK_DESTROY(slot); @@ -856,7 +1099,16 @@ int sdhci_generic_suspend(struct sdhci_slot *slot) { + /* + * We expect the MMC layer to issue initial tuning after resume. + * Otherwise, we'd need to indicate re-tuning including circuit reset + * being required at least for re-tuning modes 1 and 2 ourselves. + */ + callout_drain(&slot->retune_callout); + SDHCI_LOCK(slot); + slot->opt &= ~SDHCI_TUNING_ENABLED; sdhci_reset(slot, SDHCI_RESET_ALL); + SDHCI_UNLOCK(slot); return (0); } @@ -865,7 +1117,9 @@ int sdhci_generic_resume(struct sdhci_slot *slot) { + SDHCI_LOCK(slot); sdhci_init(slot); + SDHCI_UNLOCK(slot); return (0); } @@ -899,15 +1153,18 @@ sdhci_generic_set_uhs_timing(device_t brdev __unused, struct sdhci_slot *slot) if (slot->version < SDHCI_SPEC_300) return; + SDHCI_ASSERT_LOCKED(slot); ios = &slot->host.ios; sdhci_set_clock(slot, 0); hostctrl2 = RD2(slot, SDHCI_HOST_CONTROL2); hostctrl2 &= ~SDHCI_CTRL2_UHS_MASK; - if (ios->timing == bus_timing_mmc_hs400 || - ios->timing == bus_timing_mmc_hs400es) - hostctrl2 |= SDHCI_CTRL2_MMC_HS400; - else if (ios->clock > SD_SDR50_MAX) - hostctrl2 |= SDHCI_CTRL2_UHS_SDR104; + if (ios->clock > SD_SDR50_MAX) { + if (ios->timing == bus_timing_mmc_hs400 || + ios->timing == bus_timing_mmc_hs400es) + hostctrl2 |= SDHCI_CTRL2_MMC_HS400; + else + hostctrl2 |= SDHCI_CTRL2_UHS_SDR104; + } else if (ios->clock > SD_SDR25_MAX) hostctrl2 |= SDHCI_CTRL2_UHS_SDR50; else if (ios->clock > SD_SDR12_MAX) { @@ -1019,6 +1276,222 @@ done: return (err); } +int +sdhci_generic_tune(device_t brdev __unused, device_t reqdev, bool hs400) +{ + struct sdhci_slot *slot = device_get_ivars(reqdev); + struct mmc_ios *ios = &slot->host.ios; + struct mmc_command *tune_cmd; + struct mmc_data *tune_data; + uint32_t opcode; + int err; + + if (!(slot->opt & SDHCI_TUNING_SUPPORTED)) + return (0); + + slot->retune_ticks = slot->retune_count * hz; + opcode = MMC_SEND_TUNING_BLOCK; + SDHCI_LOCK(slot); + switch (ios->timing) { + case bus_timing_mmc_hs400: + slot_printf(slot, "HS400 must be tuned in HS200 mode\n"); + SDHCI_UNLOCK(slot); + return (EINVAL); + case bus_timing_mmc_hs200: + /* + * In HS400 mode, controllers use the data strobe line to + * latch data from the devices so periodic re-tuning isn't + * expected to be required. + */ + if (hs400) + slot->retune_ticks = 0; + opcode = MMC_SEND_TUNING_BLOCK_HS200; + break; + case bus_timing_uhs_ddr50: + case bus_timing_uhs_sdr104: + break; + case bus_timing_uhs_sdr50: + if (slot->opt & SDHCI_SDR50_NEEDS_TUNING) + break; + /* FALLTHROUGH */ + default: + SDHCI_UNLOCK(slot); + return (0); + } + + tune_cmd = slot->tune_cmd; + memset(tune_cmd, 0, sizeof(*tune_cmd)); + tune_cmd->opcode = opcode; + tune_cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC; + tune_data = tune_cmd->data = slot->tune_data; + memset(tune_data, 0, sizeof(*tune_data)); + tune_data->len = (opcode == MMC_SEND_TUNING_BLOCK_HS200 && + ios->bus_width == bus_width_8) ? MMC_TUNING_LEN_HS200 : + MMC_TUNING_LEN; + tune_data->flags = MMC_DATA_READ; + tune_data->mrq = tune_cmd->mrq = slot->tune_req; + + slot->opt &= ~SDHCI_TUNING_ENABLED; + err = sdhci_exec_tuning(slot, true); + if (err == 0) { + slot->opt |= SDHCI_TUNING_ENABLED; + slot->intmask |= sdhci_tuning_intmask(slot); + WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask); + if (slot->retune_ticks) { + callout_reset(&slot->retune_callout, slot->retune_ticks, + sdhci_retune, slot); + } + } + SDHCI_UNLOCK(slot); + return (err); +} + +int +sdhci_generic_retune(device_t brdev __unused, device_t reqdev, bool reset) +{ + struct sdhci_slot *slot = device_get_ivars(reqdev); + int err; + + if (!(slot->opt & SDHCI_TUNING_ENABLED)) + return (0); + + /* HS400 must be tuned in HS200 mode. */ + if (slot->host.ios.timing == bus_timing_mmc_hs400) + return (EINVAL); + + SDHCI_LOCK(slot); + err = sdhci_exec_tuning(slot, reset); + /* + * There are two ways sdhci_exec_tuning() can fail: + * EBUSY should not actually happen when requests are only issued + * with the host properly acquired, and + * EIO re-tuning failed (but it did work initially). + * + * In both cases, we should retry at later point if periodic re-tuning + * is enabled. Note that due to slot->retune_req not being cleared in + * these failure cases, the MMC layer should trigger another attempt at + * re-tuning with the next request anyway, though. + */ + if (slot->retune_ticks) { + callout_reset(&slot->retune_callout, slot->retune_ticks, + sdhci_retune, slot); + } + SDHCI_UNLOCK(slot); + return (err); +} + +static int +sdhci_exec_tuning(struct sdhci_slot *slot, bool reset) +{ + struct mmc_request *tune_req; + struct mmc_command *tune_cmd; + int i; + uint32_t intmask; + uint16_t hostctrl2; + u_char opt; + + SDHCI_ASSERT_LOCKED(slot); + if (slot->req != NULL) + return (EBUSY); + + /* Tuning doesn't work with DMA enabled. */ + opt = slot->opt; + slot->opt = opt & ~SDHCI_HAVE_DMA; + + /* + * Ensure that as documented, SDHCI_INT_DATA_AVAIL is the only + * kind of interrupt we receive in response to a tuning request. + */ + intmask = slot->intmask; + slot->intmask = SDHCI_INT_DATA_AVAIL; + WR4(slot, SDHCI_SIGNAL_ENABLE, SDHCI_INT_DATA_AVAIL); + + hostctrl2 = RD2(slot, SDHCI_HOST_CONTROL2); + if (reset) + hostctrl2 &= ~SDHCI_CTRL2_SAMPLING_CLOCK; + else + hostctrl2 |= SDHCI_CTRL2_SAMPLING_CLOCK; + WR2(slot, SDHCI_HOST_CONTROL2, hostctrl2 | SDHCI_CTRL2_EXEC_TUNING); + + tune_req = slot->tune_req; + tune_cmd = slot->tune_cmd; + for (i = 0; i < MMC_TUNING_MAX; i++) { + memset(tune_req, 0, sizeof(*tune_req)); +#ifdef __rtems__ + rtems_binary_semaphore_init(&tune_req->req_done, + "sdhci_req_done"); +#endif /* __rtems__ */ + tune_req->cmd = tune_cmd; + tune_req->done = sdhci_req_wakeup; + tune_req->done_data = slot; + slot->req = tune_req; + slot->flags = 0; + sdhci_start(slot); +#ifndef __rtems__ + while (!(tune_req->flags & MMC_REQ_DONE)) + msleep(tune_req, &slot->mtx, 0, "sdhciet", 0); +#else /* __rtems__ */ + rtems_binary_semaphore_wait(&tune_req->req_done); + rtems_binary_semaphore_destroy(&tune_req->req_done); +#endif /* __rtems__ */ + if (!(tune_req->flags & MMC_TUNE_DONE)) + break; + hostctrl2 = RD2(slot, SDHCI_HOST_CONTROL2); + if (!(hostctrl2 & SDHCI_CTRL2_EXEC_TUNING)) + break; + if (tune_cmd->opcode == MMC_SEND_TUNING_BLOCK) + DELAY(1000); + } + + slot->opt = opt; + slot->intmask = intmask; + WR4(slot, SDHCI_SIGNAL_ENABLE, intmask); + + if ((hostctrl2 & (SDHCI_CTRL2_EXEC_TUNING | + SDHCI_CTRL2_SAMPLING_CLOCK)) == SDHCI_CTRL2_SAMPLING_CLOCK) { + slot->retune_req = 0; + return (0); + } + + slot_printf(slot, "Tuning failed, using fixed sampling clock\n"); + WR2(slot, SDHCI_HOST_CONTROL2, hostctrl2 & ~(SDHCI_CTRL2_EXEC_TUNING | + SDHCI_CTRL2_SAMPLING_CLOCK)); + sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + return (EIO); +} + +static void +sdhci_retune(void *arg) +{ + struct sdhci_slot *slot = arg; + + slot->retune_req |= SDHCI_RETUNE_REQ_NEEDED; +} + +#ifdef MMCCAM +static void +sdhci_req_done(struct sdhci_slot *slot) +{ + union ccb *ccb; + + if (__predict_false(sdhci_debug > 1)) + slot_printf(slot, "%s\n", __func__); + if (slot->ccb != NULL && slot->curcmd != NULL) { + callout_stop(&slot->timeout_callout); + ccb = slot->ccb; + slot->ccb = NULL; + slot->curcmd = NULL; + + /* Tell CAM the request is finished */ + struct ccb_mmcio *mmcio; + mmcio = &ccb->mmcio; + + ccb->ccb_h.status = + (mmcio->cmd.error == 0 ? CAM_REQ_CMP : CAM_REQ_CMP_ERR); + xpt_done(ccb); + } +} +#else static void sdhci_req_done(struct sdhci_slot *slot) { @@ -1032,6 +1505,21 @@ sdhci_req_done(struct sdhci_slot *slot) req->done(req); } } +#endif + +static void +sdhci_req_wakeup(struct mmc_request *req) +{ +#ifndef __rtems__ + struct sdhci_slot *slot; + + slot = req->done_data; + req->flags |= MMC_REQ_DONE; + wakeup(req); +#else /* __rtems__ */ + rtems_binary_semaphore_post(&req->req_done); +#endif /* __rtems__ */ +} static void sdhci_timeout(void *arg) @@ -1039,13 +1527,13 @@ sdhci_timeout(void *arg) struct sdhci_slot *slot = arg; if (slot->curcmd != NULL) { - slot_printf(slot, " Controller timeout\n"); + slot_printf(slot, "Controller timeout\n"); sdhci_dumpregs(slot); sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA); slot->curcmd->error = MMC_ERR_TIMEOUT; sdhci_req_done(slot); } else { - slot_printf(slot, " Spurious timeout - no active command\n"); + slot_printf(slot, "Spurious timeout - no active command\n"); } } @@ -1062,8 +1550,16 @@ sdhci_set_transfer_mode(struct sdhci_slot *slot, struct mmc_data *data) mode |= SDHCI_TRNS_MULTI; if (data->flags & MMC_DATA_READ) mode |= SDHCI_TRNS_READ; +#ifdef MMCCAM + struct ccb_mmcio *mmcio; + mmcio = &slot->ccb->mmcio; + if (mmcio->stop.opcode == MMC_STOP_TRANSMISSION + && !(slot->quirks & SDHCI_QUIRK_BROKEN_AUTO_STOP)) + mode |= SDHCI_TRNS_ACMD12; +#else if (slot->req->stop && !(slot->quirks & SDHCI_QUIRK_BROKEN_AUTO_STOP)) mode |= SDHCI_TRNS_ACMD12; +#endif if (slot->flags & SDHCI_USE_DMA) mode |= SDHCI_TRNS_DMA; @@ -1096,6 +1592,9 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd) if (!SDHCI_GET_CARD_PRESENT(slot->bus, slot) || slot->power == 0 || slot->clock == 0) { + slot_printf(slot, + "Cannot issue a command (power=%d clock=%d)", + slot->power, slot->clock); cmd->error = MMC_ERR_FAILED; sdhci_req_done(slot); return; @@ -1103,10 +1602,20 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd) /* Always wait for free CMD bus. */ mask = SDHCI_CMD_INHIBIT; /* Wait for free DAT if we have data or busy signal. */ - if (cmd->data || (cmd->flags & MMC_RSP_BUSY)) + if (cmd->data != NULL || (cmd->flags & MMC_RSP_BUSY)) mask |= SDHCI_DAT_INHIBIT; - /* We shouldn't wait for DAT for stop commands. */ - if (cmd == slot->req->stop) + /* + * We shouldn't wait for DAT for stop commands or CMD19/CMD21. Note + * that these latter are also special in that SDHCI_CMD_DATA should + * be set below but no actual data is ever read from the controller. + */ +#ifdef MMCCAM + if (cmd == &slot->ccb->mmcio.stop || +#else + if (cmd == slot->req->stop || +#endif + __predict_false(cmd->opcode == MMC_SEND_TUNING_BLOCK || + cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)) mask &= ~SDHCI_DAT_INHIBIT; /* * Wait for bus no more then 250 ms. Typically there will be no wait @@ -1145,7 +1654,7 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd) flags |= SDHCI_CMD_CRC; if (cmd->flags & MMC_RSP_OPCODE) flags |= SDHCI_CMD_INDEX; - if (cmd->data) + if (cmd->data != NULL) flags |= SDHCI_CMD_DATA; if (cmd->opcode == MMC_STOP_TRANSMISSION) flags |= SDHCI_CMD_TYPE_ABORT; @@ -1164,6 +1673,8 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd) WR4(slot, SDHCI_ARGUMENT, cmd->arg); /* Set data transfer mode. */ sdhci_set_transfer_mode(slot, cmd->data); + if (__predict_false(sdhci_debug > 1)) + slot_printf(slot, "Starting command!\n"); /* Start command. */ WR2(slot, SDHCI_COMMAND_FLAGS, (cmd->opcode << 8) | (flags & 0xff)); /* Start timeout callout. */ @@ -1178,15 +1689,23 @@ sdhci_finish_command(struct sdhci_slot *slot) uint32_t val; uint8_t extra; + if (__predict_false(sdhci_debug > 1)) + slot_printf(slot, "%s: called, err %d flags %d\n", + __func__, slot->curcmd->error, slot->curcmd->flags); slot->cmd_done = 1; /* * Interrupt aggregation: Restore command interrupt. * Main restore point for the case when command interrupt * happened first. */ - WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask |= SDHCI_INT_RESPONSE); + if (__predict_true(slot->curcmd->opcode != MMC_SEND_TUNING_BLOCK && + slot->curcmd->opcode != MMC_SEND_TUNING_BLOCK_HS200)) + WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask |= + SDHCI_INT_RESPONSE); /* In case of error - reset host and return. */ if (slot->curcmd->error) { + if (slot->curcmd->error == MMC_ERR_BADCRC) + slot->retune_req |= SDHCI_RETUNE_REQ_RESET; sdhci_reset(slot, SDHCI_RESET_CMD); sdhci_reset(slot, SDHCI_RESET_DATA); sdhci_start(slot); @@ -1211,6 +1730,11 @@ sdhci_finish_command(struct sdhci_slot *slot) } else slot->curcmd->resp[0] = RD4(slot, SDHCI_RESPONSE); } + if (__predict_false(sdhci_debug > 1)) + printf("Resp: %02x %02x %02x %02x\n", + slot->curcmd->resp[0], slot->curcmd->resp[1], + slot->curcmd->resp[2], slot->curcmd->resp[3]); + /* If data ready - finish. */ if (slot->data_done) sdhci_start(slot); @@ -1291,6 +1815,11 @@ sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data) (data->len < 512) ? data->len : 512)); /* Set block count. */ WR2(slot, SDHCI_BLOCK_COUNT, (data->len + 511) / 512); + + if (__predict_false(sdhci_debug > 1)) + slot_printf(slot, "Block size: %02x, count %lu\n", + (unsigned int)SDHCI_MAKE_BLKSZ(DMA_BOUNDARY, (data->len < 512) ? data->len : 512), + (unsigned long)(data->len + 511) / 512); } void @@ -1322,6 +1851,8 @@ sdhci_finish_data(struct sdhci_slot *slot) slot->data_done = 1; /* If there was error - reset the host. */ if (slot->curcmd->error) { + if (slot->curcmd->error == MMC_ERR_BADCRC) + slot->retune_req |= SDHCI_RETUNE_REQ_RESET; sdhci_reset(slot, SDHCI_RESET_CMD); sdhci_reset(slot, SDHCI_RESET_DATA); sdhci_start(slot); @@ -1332,6 +1863,47 @@ sdhci_finish_data(struct sdhci_slot *slot) sdhci_start(slot); } +#ifdef MMCCAM +static void +sdhci_start(struct sdhci_slot *slot) +{ + union ccb *ccb; + + ccb = slot->ccb; + if (ccb == NULL) + return; + + struct ccb_mmcio *mmcio; + mmcio = &ccb->mmcio; + + if (!(slot->flags & CMD_STARTED)) { + slot->flags |= CMD_STARTED; + sdhci_start_command(slot, &mmcio->cmd); + return; + } + + /* + * Old stack doesn't use this! + * Enabling this code causes significant performance degradation + * and IRQ storms on BBB, Wandboard behaves fine. + * Not using this code does no harm... + if (!(slot->flags & STOP_STARTED) && mmcio->stop.opcode != 0) { + slot->flags |= STOP_STARTED; + sdhci_start_command(slot, &mmcio->stop); + return; + } + */ + if (__predict_false(sdhci_debug > 1)) + slot_printf(slot, "result: %d\n", mmcio->cmd.error); + if (mmcio->cmd.error == 0 && + (slot->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) { + sdhci_reset(slot, SDHCI_RESET_CMD); + sdhci_reset(slot, SDHCI_RESET_DATA); + } + + sdhci_req_done(slot); +} +#else static void sdhci_start(struct sdhci_slot *slot) { @@ -1352,7 +1924,7 @@ sdhci_start(struct sdhci_slot *slot) sdhci_start_command(slot, req->stop); return; } - if (sdhci_debug > 1) + if (__predict_false(sdhci_debug > 1)) slot_printf(slot, "result: %d\n", req->cmd->error); if (!req->cmd->error && ((slot->curcmd == req->stop && @@ -1364,6 +1936,7 @@ sdhci_start(struct sdhci_slot *slot) sdhci_req_done(slot); } +#endif int sdhci_generic_request(device_t brdev __unused, device_t reqdev, @@ -1376,7 +1949,7 @@ sdhci_generic_request(device_t brdev __unused, device_t reqdev, SDHCI_UNLOCK(slot); return (EBUSY); } - if (sdhci_debug > 1) { + if (__predict_false(sdhci_debug > 1)) { slot_printf(slot, "CMD%u arg %#x flags %#x dlen %u dflags %#x\n", req->cmd->opcode, req->cmd->arg, req->cmd->flags, @@ -1495,6 +2068,15 @@ sdhci_data_irq(struct sdhci_slot *slot, uint32_t intmask) goto done; } + /* Handle tuning completion interrupt. */ + if (__predict_false((intmask & SDHCI_INT_DATA_AVAIL) && + (slot->curcmd->opcode == MMC_SEND_TUNING_BLOCK || + slot->curcmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))) { + slot->req->flags |= MMC_TUNE_DONE; + sdhci_finish_command(slot); + sdhci_finish_data(slot); + return; + } /* Handle PIO interrupt. */ if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) { if ((slot->opt & SDHCI_PLATFORM_TRANSFER) && @@ -1587,9 +2169,21 @@ sdhci_generic_intr(struct sdhci_slot *slot) SDHCI_UNLOCK(slot); return; } - if (sdhci_debug > 2) + if (__predict_false(sdhci_debug > 2)) slot_printf(slot, "Interrupt %#x\n", intmask); + /* Handle tuning error interrupt. */ + if (__predict_false(intmask & SDHCI_INT_TUNEERR)) { + slot_printf(slot, "Tuning error indicated\n"); + slot->retune_req |= SDHCI_RETUNE_REQ_RESET; + if (slot->curcmd) { + slot->curcmd->error = MMC_ERR_BADCRC; + sdhci_finish_command(slot); + } + } + /* Handle re-tuning interrupt. */ + if (__predict_false(intmask & SDHCI_INT_RETUNE)) + slot->retune_req |= SDHCI_RETUNE_REQ_NEEDED; /* Handle card presence interrupts. */ if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { present = (intmask & SDHCI_INT_CARD_INSERT) != 0; @@ -1602,7 +2196,6 @@ sdhci_generic_intr(struct sdhci_slot *slot) WR4(slot, SDHCI_INT_STATUS, intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)); sdhci_handle_card_present_locked(slot, present); - intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE); } /* Handle command interrupts. */ if (intmask & SDHCI_INT_CMD_MASK) { @@ -1621,16 +2214,14 @@ sdhci_generic_intr(struct sdhci_slot *slot) WR4(slot, SDHCI_INT_STATUS, SDHCI_INT_ACMD12ERR); sdhci_acmd_irq(slot); } - intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK); - intmask &= ~SDHCI_INT_ACMD12ERR; - intmask &= ~SDHCI_INT_ERROR; /* Handle bus power interrupt. */ if (intmask & SDHCI_INT_BUS_POWER) { WR4(slot, SDHCI_INT_STATUS, SDHCI_INT_BUS_POWER); - slot_printf(slot, - "Card is consuming too much power!\n"); - intmask &= ~SDHCI_INT_BUS_POWER; + slot_printf(slot, "Card is consuming too much power!\n"); } + intmask &= ~(SDHCI_INT_ERROR | SDHCI_INT_TUNEERR | SDHCI_INT_RETUNE | + SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE | SDHCI_INT_CMD_MASK | + SDHCI_INT_DATA_MASK | SDHCI_INT_ACMD12ERR | SDHCI_INT_BUS_POWER); /* The rest is unknown. */ if (intmask) { WR4(slot, SDHCI_INT_STATUS, intmask); @@ -1684,6 +2275,19 @@ sdhci_generic_read_ivar(device_t bus, device_t child, int which, case MMCBR_IVAR_VDD: *result = slot->host.ios.vdd; break; + case MMCBR_IVAR_RETUNE_REQ: + if (slot->opt & SDHCI_TUNING_ENABLED) { + if (slot->retune_req & SDHCI_RETUNE_REQ_RESET) { + *result = retune_req_reset; + break; + } + if (slot->retune_req & SDHCI_RETUNE_REQ_NEEDED) { + *result = retune_req_normal; + break; + } + } + *result = retune_req_none; + break; case MMCBR_IVAR_VCCQ: *result = slot->host.ios.vccq; break; @@ -1694,6 +2298,16 @@ sdhci_generic_read_ivar(device_t bus, device_t child, int which, *result = slot->host.ios.timing; break; case MMCBR_IVAR_MAX_DATA: + /* + * Re-tuning modes 1 and 2 restrict the maximum data length + * per read/write command to 4 MiB. + */ + if (slot->opt & SDHCI_TUNING_ENABLED && + (slot->retune_mode == SDHCI_RETUNE_MODE_1 || + slot->retune_mode == SDHCI_RETUNE_MODE_2)) { + *result = 4 * 1024 * 1024 / MMC_SECTOR_SIZE; + break; + } *result = 65535; break; case MMCBR_IVAR_MAX_BUSY_TIMEOUT: @@ -1714,6 +2328,8 @@ sdhci_generic_write_ivar(device_t bus, device_t child, int which, uint32_t clock, max_clock; int i; + if (sdhci_debug > 1) + slot_printf(slot, "%s: var=%d\n", __func__, which); switch (which) { default: return (EINVAL); @@ -1774,9 +2390,324 @@ sdhci_generic_write_ivar(device_t bus, device_t child, int which, case MMCBR_IVAR_F_MIN: case MMCBR_IVAR_F_MAX: case MMCBR_IVAR_MAX_DATA: + case MMCBR_IVAR_RETUNE_REQ: return (EINVAL); } return (0); } +#ifdef MMCCAM +void +sdhci_cam_start_slot(struct sdhci_slot *slot) +{ + if ((slot->devq = cam_simq_alloc(1)) == NULL) { + goto fail; + } + + mtx_init(&slot->sim_mtx, "sdhcisim", NULL, MTX_DEF); + slot->sim = cam_sim_alloc(sdhci_cam_action, sdhci_cam_poll, + "sdhci_slot", slot, device_get_unit(slot->bus), + &slot->sim_mtx, 1, 1, slot->devq); + + if (slot->sim == NULL) { + cam_simq_free(slot->devq); + slot_printf(slot, "cannot allocate CAM SIM\n"); + goto fail; + } + + mtx_lock(&slot->sim_mtx); + if (xpt_bus_register(slot->sim, slot->bus, 0) != 0) { + slot_printf(slot, + "cannot register SCSI pass-through bus\n"); + cam_sim_free(slot->sim, FALSE); + cam_simq_free(slot->devq); + mtx_unlock(&slot->sim_mtx); + goto fail; + } + + mtx_unlock(&slot->sim_mtx); + /* End CAM-specific init */ + slot->card_present = 0; + sdhci_card_task(slot, 0); + return; + +fail: + if (slot->sim != NULL) { + mtx_lock(&slot->sim_mtx); + xpt_bus_deregister(cam_sim_path(slot->sim)); + cam_sim_free(slot->sim, FALSE); + mtx_unlock(&slot->sim_mtx); + } + + if (slot->devq != NULL) + cam_simq_free(slot->devq); +} + +static void +sdhci_cam_handle_mmcio(struct cam_sim *sim, union ccb *ccb) +{ + struct sdhci_slot *slot; + + slot = cam_sim_softc(sim); + + sdhci_cam_request(slot, ccb); +} + +void +sdhci_cam_action(struct cam_sim *sim, union ccb *ccb) +{ + struct sdhci_slot *slot; + + slot = cam_sim_softc(sim); + if (slot == NULL) { + ccb->ccb_h.status = CAM_SEL_TIMEOUT; + xpt_done(ccb); + return; + } + + mtx_assert(&slot->sim_mtx, MA_OWNED); + + switch (ccb->ccb_h.func_code) { + case XPT_PATH_INQ: + { + struct ccb_pathinq *cpi; + + cpi = &ccb->cpi; + cpi->version_num = 1; + cpi->hba_inquiry = 0; + cpi->target_sprt = 0; + cpi->hba_misc = PIM_NOBUSRESET | PIM_SEQSCAN; + cpi->hba_eng_cnt = 0; + cpi->max_target = 0; + cpi->max_lun = 0; + cpi->initiator_id = 1; + cpi->maxio = MAXPHYS; + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, "Deglitch Networks", 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 = 100; /* XXX WTF? */ + cpi->protocol = PROTO_MMCSD; + cpi->protocol_version = SCSI_REV_0; + cpi->transport = XPORT_MMCSD; + cpi->transport_version = 0; + + cpi->ccb_h.status = CAM_REQ_CMP; + break; + } + case XPT_GET_TRAN_SETTINGS: + { + struct ccb_trans_settings *cts = &ccb->cts; + + if (sdhci_debug > 1) + slot_printf(slot, "Got XPT_GET_TRAN_SETTINGS\n"); + + cts->protocol = PROTO_MMCSD; + cts->protocol_version = 1; + cts->transport = XPORT_MMCSD; + cts->transport_version = 1; + cts->xport_specific.valid = 0; + cts->proto_specific.mmc.host_ocr = slot->host.host_ocr; + cts->proto_specific.mmc.host_f_min = slot->host.f_min; + cts->proto_specific.mmc.host_f_max = slot->host.f_max; + cts->proto_specific.mmc.host_caps = slot->host.caps; + memcpy(&cts->proto_specific.mmc.ios, &slot->host.ios, sizeof(struct mmc_ios)); + ccb->ccb_h.status = CAM_REQ_CMP; + break; + } + case XPT_SET_TRAN_SETTINGS: + { + if (sdhci_debug > 1) + slot_printf(slot, "Got XPT_SET_TRAN_SETTINGS\n"); + sdhci_cam_settran_settings(slot, ccb); + ccb->ccb_h.status = CAM_REQ_CMP; + break; + } + case XPT_RESET_BUS: + if (sdhci_debug > 1) + slot_printf(slot, "Got XPT_RESET_BUS, ACK it...\n"); + ccb->ccb_h.status = CAM_REQ_CMP; + break; + case XPT_MMC_IO: + /* + * Here is the HW-dependent part of + * sending the command to the underlying h/w + * At some point in the future an interrupt comes. + * Then the request will be marked as completed. + */ + if (__predict_false(sdhci_debug > 1)) + slot_printf(slot, "Got XPT_MMC_IO\n"); + ccb->ccb_h.status = CAM_REQ_INPROG; + + sdhci_cam_handle_mmcio(sim, ccb); + return; + /* NOTREACHED */ + break; + default: + ccb->ccb_h.status = CAM_REQ_INVALID; + break; + } + xpt_done(ccb); + return; +} + +void +sdhci_cam_poll(struct cam_sim *sim) +{ + return; +} + +int sdhci_cam_get_possible_host_clock(struct sdhci_slot *slot, int proposed_clock) { + int max_clock, clock, i; + + if (proposed_clock == 0) + return 0; + max_clock = slot->max_clk; + clock = max_clock; + + if (slot->version < SDHCI_SPEC_300) { + for (i = 0; i < SDHCI_200_MAX_DIVIDER; + i <<= 1) { + if (clock <= proposed_clock) + break; + clock >>= 1; + } + } else { + for (i = 0; i < SDHCI_300_MAX_DIVIDER; + i += 2) { + if (clock <= proposed_clock) + break; + clock = max_clock / (i + 2); + } + } + return clock; +} + +int +sdhci_cam_settran_settings(struct sdhci_slot *slot, union ccb *ccb) +{ + struct mmc_ios *ios; + struct mmc_ios *new_ios; + struct ccb_trans_settings_mmc *cts; + + ios = &slot->host.ios; + + cts = &ccb->cts.proto_specific.mmc; + new_ios = &cts->ios; + + /* Update only requested fields */ + if (cts->ios_valid & MMC_CLK) { + ios->clock = sdhci_cam_get_possible_host_clock(slot, new_ios->clock); + slot_printf(slot, "Clock => %d\n", ios->clock); + } + if (cts->ios_valid & MMC_VDD) { + ios->vdd = new_ios->vdd; + slot_printf(slot, "VDD => %d\n", ios->vdd); + } + if (cts->ios_valid & MMC_CS) { + ios->chip_select = new_ios->chip_select; + slot_printf(slot, "CS => %d\n", ios->chip_select); + } + if (cts->ios_valid & MMC_BW) { + ios->bus_width = new_ios->bus_width; + slot_printf(slot, "Bus width => %d\n", ios->bus_width); + } + if (cts->ios_valid & MMC_PM) { + ios->power_mode = new_ios->power_mode; + slot_printf(slot, "Power mode => %d\n", ios->power_mode); + } + if (cts->ios_valid & MMC_BT) { + ios->timing = new_ios->timing; + slot_printf(slot, "Timing => %d\n", ios->timing); + } + if (cts->ios_valid & MMC_BM) { + ios->bus_mode = new_ios->bus_mode; + slot_printf(slot, "Bus mode => %d\n", ios->bus_mode); + } + + /* XXX Provide a way to call a chip-specific IOS update, required for TI */ + return (sdhci_cam_update_ios(slot)); +} + +int +sdhci_cam_update_ios(struct sdhci_slot *slot) +{ + struct mmc_ios *ios = &slot->host.ios; + + slot_printf(slot, "%s: power_mode=%d, clk=%d, bus_width=%d, timing=%d\n", + __func__, ios->power_mode, ios->clock, ios->bus_width, ios->timing); + SDHCI_LOCK(slot); + /* Do full reset on bus power down to clear from any state. */ + if (ios->power_mode == power_off) { + WR4(slot, SDHCI_SIGNAL_ENABLE, 0); + sdhci_init(slot); + } + /* Configure the bus. */ + sdhci_set_clock(slot, ios->clock); + sdhci_set_power(slot, (ios->power_mode == power_off) ? 0 : ios->vdd); + if (ios->bus_width == bus_width_8) { + slot->hostctrl |= SDHCI_CTRL_8BITBUS; + slot->hostctrl &= ~SDHCI_CTRL_4BITBUS; + } else if (ios->bus_width == bus_width_4) { + slot->hostctrl &= ~SDHCI_CTRL_8BITBUS; + slot->hostctrl |= SDHCI_CTRL_4BITBUS; + } else if (ios->bus_width == bus_width_1) { + slot->hostctrl &= ~SDHCI_CTRL_8BITBUS; + slot->hostctrl &= ~SDHCI_CTRL_4BITBUS; + } else { + panic("Invalid bus width: %d", ios->bus_width); + } + if (ios->timing == bus_timing_hs && + !(slot->quirks & SDHCI_QUIRK_DONT_SET_HISPD_BIT)) + slot->hostctrl |= SDHCI_CTRL_HISPD; + else + slot->hostctrl &= ~SDHCI_CTRL_HISPD; + WR1(slot, SDHCI_HOST_CONTROL, slot->hostctrl); + /* Some controllers like reset after bus changes. */ + if(slot->quirks & SDHCI_QUIRK_RESET_ON_IOS) + sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + + SDHCI_UNLOCK(slot); + return (0); +} + +int +sdhci_cam_request(struct sdhci_slot *slot, union ccb *ccb) +{ + struct ccb_mmcio *mmcio; + + mmcio = &ccb->mmcio; + + SDHCI_LOCK(slot); +/* if (slot->req != NULL) { + SDHCI_UNLOCK(slot); + return (EBUSY); + } +*/ + if (__predict_false(sdhci_debug > 1)) { + slot_printf(slot, "CMD%u arg %#x flags %#x dlen %u dflags %#x\n", + mmcio->cmd.opcode, mmcio->cmd.arg, mmcio->cmd.flags, + mmcio->cmd.data != NULL ? (unsigned int) mmcio->cmd.data->len : 0, + mmcio->cmd.data != NULL ? mmcio->cmd.data->flags: 0); + } + if (mmcio->cmd.data != NULL) { + if (mmcio->cmd.data->len == 0 || mmcio->cmd.data->flags == 0) + panic("data->len = %d, data->flags = %d -- something is b0rked", + (int)mmcio->cmd.data->len, mmcio->cmd.data->flags); + } + slot->ccb = ccb; + slot->flags = 0; + sdhci_start(slot); + SDHCI_UNLOCK(slot); + if (dumping) { + while (slot->ccb != NULL) { + sdhci_generic_intr(slot); + DELAY(10); + } + } + return (0); +} +#endif /* MMCCAM */ + MODULE_VERSION(sdhci, 1); diff --git a/freebsd/sys/dev/sdhci/sdhci.h b/freebsd/sys/dev/sdhci/sdhci.h index 814f81ed..73aa84b6 100644 --- a/freebsd/sys/dev/sdhci/sdhci.h +++ b/freebsd/sys/dev/sdhci/sdhci.h @@ -28,6 +28,8 @@ #ifndef __SDHCI_H__ #define __SDHCI_H__ +#include <rtems/bsd/local/opt_mmccam.h> + #define DMA_BLOCK_SIZE 4096 #define DMA_BOUNDARY 0 /* DMA reload every 4K */ @@ -235,6 +237,11 @@ #define SDHCI_HOST_CONTROL2 0x3E #define SDHCI_CTRL2_PRESET_VALUE 0x8000 #define SDHCI_CTRL2_ASYNC_INTR 0x4000 +#define SDHCI_CTRL2_64BIT_ENABLE 0x2000 +#define SDHCI_CTRL2_HOST_V4_ENABLE 0x1000 +#define SDHCI_CTRL2_CMD23_ENABLE 0x0800 +#define SDHCI_CTRL2_ADMA2_LENGTH_MODE 0x0400 +#define SDHCI_CTRL2_UHS2_IFACE_ENABLE 0x0100 #define SDHCI_CTRL2_SAMPLING_CLOCK 0x0080 #define SDHCI_CTRL2_EXEC_TUNING 0x0040 #define SDHCI_CTRL2_DRIVER_TYPE_MASK 0x0030 @@ -319,6 +326,8 @@ #define SDHCI_SPEC_200 1 #define SDHCI_SPEC_300 2 #define SDHCI_SPEC_400 3 +#define SDHCI_SPEC_410 4 +#define SDHCI_SPEC_420 5 SYSCTL_DECL(_hw_sdhci); @@ -326,6 +335,7 @@ extern u_int sdhci_quirk_clear; extern u_int sdhci_quirk_set; struct sdhci_slot { + struct mtx mtx; /* Slot mutex */ u_int quirks; /* Chip specific quirks */ u_int caps; /* Override SDHCI_CAPABILITIES */ u_int caps2; /* Override SDHCI_CAPABILITIES2 */ @@ -336,6 +346,10 @@ struct sdhci_slot { #define SDHCI_HAVE_DMA 0x01 #define SDHCI_PLATFORM_TRANSFER 0x02 #define SDHCI_NON_REMOVABLE 0x04 +#define SDHCI_TUNING_SUPPORTED 0x08 +#define SDHCI_TUNING_ENABLED 0x10 +#define SDHCI_SDR50_NEEDS_TUNING 0x20 +#define SDHCI_SLOT_EMBEDDED 0x40 u_char version; int timeout; /* Transfer timeout */ uint32_t max_clk; /* Max possible freq */ @@ -349,14 +363,27 @@ struct sdhci_slot { card_delayed_task;/* Card insert delayed task */ struct callout card_poll_callout;/* Card present polling callout */ struct callout timeout_callout;/* Card command/data response timeout */ + struct callout retune_callout; /* Re-tuning mode 1 callout */ struct mmc_host host; /* Host parameters */ struct mmc_request *req; /* Current request */ struct mmc_command *curcmd; /* Current command of current request */ + struct mmc_request *tune_req; /* Tuning request */ + struct mmc_command *tune_cmd; /* Tuning command of tuning request */ + struct mmc_data *tune_data; /* Tuning data of tuning command */ + uint32_t retune_ticks; /* Re-tuning callout ticks [hz] */ uint32_t intmask; /* Current interrupt mask */ uint32_t clock; /* Current clock freq. */ size_t offset; /* Data buffer offset */ uint8_t hostctrl; /* Current host control register */ + uint8_t retune_count; /* Controller re-tuning count [s] */ + uint8_t retune_mode; /* Controller re-tuning mode */ +#define SDHCI_RETUNE_MODE_1 0x00 +#define SDHCI_RETUNE_MODE_2 0x01 +#define SDHCI_RETUNE_MODE_3 0x02 + uint8_t retune_req; /* Re-tuning request status */ +#define SDHCI_RETUNE_REQ_NEEDED 0x01 /* Re-tuning w/o circuit reset needed */ +#define SDHCI_RETUNE_REQ_RESET 0x02 /* Re-tuning w/ circuit reset needed */ u_char power; /* Current power */ u_char bus_busy; /* Bus busy status */ u_char cmd_done; /* CMD command part done flag */ @@ -366,7 +393,15 @@ struct sdhci_slot { #define STOP_STARTED 2 #define SDHCI_USE_DMA 4 /* Use DMA for this req. */ #define PLATFORM_DATA_STARTED 8 /* Data xfer is handled by platform */ - struct mtx mtx; /* Slot mutex */ + +#ifdef MMCCAM + /* CAM stuff */ + union ccb *ccb; + struct cam_devq *devq; + struct cam_sim *sim; + struct mtx sim_mtx; + u_char card_present; /* XXX Maybe derive this from elsewhere? */ +#endif }; int sdhci_generic_read_ivar(device_t bus, device_t child, int which, @@ -381,7 +416,9 @@ int sdhci_cleanup_slot(struct sdhci_slot *slot); int sdhci_generic_suspend(struct sdhci_slot *slot); int sdhci_generic_resume(struct sdhci_slot *slot); int sdhci_generic_update_ios(device_t brdev, device_t reqdev); +int sdhci_generic_tune(device_t brdev, device_t reqdev, bool hs400); int sdhci_generic_switch_vccq(device_t brdev, device_t reqdev); +int sdhci_generic_retune(device_t brdev, device_t reqdev, bool reset); int sdhci_generic_request(device_t brdev, device_t reqdev, struct mmc_request *req); int sdhci_generic_get_ro(device_t brdev, device_t reqdev); @@ -393,4 +430,9 @@ bool sdhci_generic_get_card_present(device_t brdev, struct sdhci_slot *slot); void sdhci_generic_set_uhs_timing(device_t brdev, struct sdhci_slot *slot); void sdhci_handle_card_present(struct sdhci_slot *slot, bool is_present); +#ifdef MMCCAM +/* CAM-related */ +void sdhci_cam_start_slot(struct sdhci_slot *slot); +#endif + #endif /* __SDHCI_H__ */ diff --git a/freebsd/sys/dev/tsec/if_tsec.c b/freebsd/sys/dev/tsec/if_tsec.c index d76c8f10..b7edc948 100644 --- a/freebsd/sys/dev/tsec/if_tsec.c +++ b/freebsd/sys/dev/tsec/if_tsec.c @@ -39,7 +39,6 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> #include <sys/bus.h> -#include <sys/bus_dma.h> #include <sys/endian.h> #include <sys/mbuf.h> #include <sys/kernel.h> diff --git a/freebsd/sys/dev/usb/controller/saf1761_otg.c b/freebsd/sys/dev/usb/controller/saf1761_otg.c index f9d28a63..e1ce5eb6 100644 --- a/freebsd/sys/dev/usb/controller/saf1761_otg.c +++ b/freebsd/sys/dev/usb/controller/saf1761_otg.c @@ -525,9 +525,6 @@ saf1761_host_bulk_data_rx(struct saf1761_otg_softc *sc, struct saf1761_otg_td *t DPRINTFN(5, "STATUS=0x%08x\n", status); if (status & SOTG_PTD_DW3_ACTIVE) { -#ifndef __rtems__ - goto busy; -#else /* __rtems__ */ temp = saf1761_peek_host_status_le_4(sc, pdt_addr + SOTG_PTD_DW0); if (temp & SOTG_PTD_DW0_VALID) { @@ -547,7 +544,6 @@ saf1761_host_bulk_data_rx(struct saf1761_otg_softc *sc, struct saf1761_otg_td *t goto complete; } } -#endif /* __rtems__ */ } else if (status & SOTG_PTD_DW3_HALTED) { if (!(status & SOTG_PTD_DW3_ERRORS)) td->error_stall = 1; @@ -591,9 +587,7 @@ saf1761_host_bulk_data_rx(struct saf1761_otg_softc *sc, struct saf1761_otg_td *t } saf1761_host_channel_free(sc, td); } -#ifdef __rtems__ retry: -#endif /* __rtems__ */ if (saf1761_host_channel_alloc(sc, td)) goto busy; @@ -1623,10 +1617,8 @@ saf1761_otg_filter_interrupt(void *arg) (void) SAF1761_READ_LE_4(sc, SOTG_INT_PTD_DONE_PTD); (void) SAF1761_READ_LE_4(sc, SOTG_ISO_PTD_DONE_PTD); -#ifdef __rtems__ DPRINTFN(9, "HCINTERRUPT=0x%08x DCINTERRUPT=0x%08x\n", hcstat, status); -#endif /* __rtems__ */ if (status & SOTG_DCINTERRUPT_IEPSOF) { if ((sc->sc_host_async_busy_map[1] | sc->sc_host_async_busy_map[0] | sc->sc_host_intr_busy_map[1] | sc->sc_host_intr_busy_map[0] | @@ -2496,13 +2488,12 @@ saf1761_otg_init(struct saf1761_otg_softc *sc) #ifdef __rtems__ SAF1761_WRITE_LE_4(sc, SOTG_CTRL_SET_CLR, SOTG_CTRL_SET(SOTG_CTRL_SEL_CP_EXT | SOTG_CTRL_VBUS_DRV)); -#else /* __rtems__ */ +#else SAF1761_WRITE_LE_4(sc, SOTG_CTRL_SET_CLR, SOTG_CTRL_SET(SOTG_CTRL_SW_SEL_HC_DC | SOTG_CTRL_BDIS_ACON_EN | SOTG_CTRL_SEL_CP_EXT | SOTG_CTRL_VBUS_DRV)); -#endif /* __rtems__ */ - +#endif /* disable device address */ SAF1761_WRITE_LE_4(sc, SOTG_ADDRESS, 0); diff --git a/freebsd/sys/dev/usb/wlan/if_rsu.c b/freebsd/sys/dev/usb/wlan/if_rsu.c index 659e178a..2415d2f2 100644 --- a/freebsd/sys/dev/usb/wlan/if_rsu.c +++ b/freebsd/sys/dev/usb/wlan/if_rsu.c @@ -2036,6 +2036,8 @@ rsu_hwrssi_to_rssi(struct rsu_softc *sc, int hw_rssi) return (v); } +CTASSERT(MCLBYTES > sizeof(struct ieee80211_frame)); + static void rsu_event_survey(struct rsu_softc *sc, uint8_t *buf, int len) { @@ -2044,28 +2046,31 @@ rsu_event_survey(struct rsu_softc *sc, uint8_t *buf, int len) struct ndis_wlan_bssid_ex *bss; struct ieee80211_rx_stats rxs; struct mbuf *m; - int pktlen; + uint32_t ieslen; + uint32_t pktlen; if (__predict_false(len < sizeof(*bss))) return; bss = (struct ndis_wlan_bssid_ex *)buf; - if (__predict_false(len < sizeof(*bss) + le32toh(bss->ieslen))) + ieslen = le32toh(bss->ieslen); + /* range check length of information element */ + if (__predict_false(ieslen > (uint32_t)(len - sizeof(*bss)))) return; RSU_DPRINTF(sc, RSU_DEBUG_SCAN, "%s: found BSS %s: len=%d chan=%d inframode=%d " "networktype=%d privacy=%d, RSSI=%d\n", __func__, - ether_sprintf(bss->macaddr), le32toh(bss->len), + ether_sprintf(bss->macaddr), ieslen, le32toh(bss->config.dsconfig), le32toh(bss->inframode), le32toh(bss->networktype), le32toh(bss->privacy), le32toh(bss->rssi)); /* Build a fake beacon frame to let net80211 do all the parsing. */ /* XXX TODO: just call the new scan API methods! */ - pktlen = sizeof(*wh) + le32toh(bss->ieslen); - if (__predict_false(pktlen > MCLBYTES)) + if (__predict_false(ieslen > (size_t)(MCLBYTES - sizeof(*wh)))) return; + pktlen = sizeof(*wh) + ieslen; m = m_get2(pktlen, M_NOWAIT, MT_DATA, M_PKTHDR); if (__predict_false(m == NULL)) return; @@ -2078,7 +2083,7 @@ rsu_event_survey(struct rsu_softc *sc, uint8_t *buf, int len) IEEE80211_ADDR_COPY(wh->i_addr2, bss->macaddr); IEEE80211_ADDR_COPY(wh->i_addr3, bss->macaddr); *(uint16_t *)wh->i_seq = 0; - memcpy(&wh[1], (uint8_t *)&bss[1], le32toh(bss->ieslen)); + memcpy(&wh[1], (uint8_t *)&bss[1], ieslen); /* Finalize mbuf. */ m->m_pkthdr.len = m->m_len = pktlen; @@ -3378,6 +3383,8 @@ rsu_fw_loadsection(struct rsu_softc *sc, const uint8_t *buf, int len) return (0); } +CTASSERT(sizeof(size_t) >= sizeof(uint32_t)); + static int rsu_load_firmware(struct rsu_softc *sc) { @@ -3385,7 +3392,7 @@ rsu_load_firmware(struct rsu_softc *sc) struct r92s_fw_priv *dmem; struct ieee80211com *ic = &sc->sc_ic; const uint8_t *imem, *emem; - int imemsz, ememsz; + uint32_t imemsz, ememsz; const struct firmware *fw; size_t size; uint32_t reg; @@ -3437,7 +3444,8 @@ rsu_load_firmware(struct rsu_softc *sc) imemsz = le32toh(hdr->imemsz); ememsz = le32toh(hdr->sramsz); /* Check that all FW sections fit in image. */ - if (size < sizeof(*hdr) + imemsz + ememsz) { + if (imemsz > (size_t)(size - sizeof(*hdr)) || + ememsz > (size_t)(size - sizeof(*hdr) - imemsz)) { device_printf(sc->sc_dev, "firmware too short\n"); error = EINVAL; goto fail; diff --git a/freebsd/sys/dev/usb/wlan/if_zyd.c b/freebsd/sys/dev/usb/wlan/if_zyd.c index 3ae5616f..e4243d8f 100644 --- a/freebsd/sys/dev/usb/wlan/if_zyd.c +++ b/freebsd/sys/dev/usb/wlan/if_zyd.c @@ -650,11 +650,12 @@ zyd_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct zyd_notif_retry *retry = (struct zyd_notif_retry *)cmd->data; + uint16_t count = le16toh(retry->count); DPRINTF(sc, ZYD_DEBUG_TX_PROC, "retry intr: rate=0x%x addr=%s count=%d (0x%x)\n", le16toh(retry->rate), ether_sprintf(retry->macaddr), - le16toh(retry->count)&0xff, le16toh(retry->count)); + count & 0xff, count); /* * Find the node to which the packet was sent and @@ -666,13 +667,12 @@ zyd_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) if (ni != NULL) { struct ieee80211_ratectl_tx_status *txs = &sc->sc_txs; - int retrycnt = - (int)(le16toh(retry->count) & 0xff); + int retrycnt = count & 0xff; txs->flags = IEEE80211_RATECTL_STATUS_LONG_RETRY; txs->long_retries = retrycnt; - if (le16toh(retry->count) & 0x100) { + if (count & 0x100) { txs->status = IEEE80211_RATECTL_TX_FAIL_LONG; } else { @@ -684,7 +684,7 @@ zyd_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) ieee80211_ratectl_tx_complete(ni, txs); ieee80211_free_node(ni); } - if (le16toh(retry->count) & 0x100) + if (count & 0x100) /* too many retries */ if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, 1); diff --git a/freebsd/sys/kern/init_main.c b/freebsd/sys/kern/init_main.c index 467888b2..f211b363 100644 --- a/freebsd/sys/kern/init_main.c +++ b/freebsd/sys/kern/init_main.c @@ -384,8 +384,7 @@ SYSINIT(diagwarn2, SI_SUB_LAST, SI_ORDER_THIRD + 2, #endif static int -null_fetch_syscall_args(struct thread *td __unused, - struct syscall_args *sa __unused) +null_fetch_syscall_args(struct thread *td __unused) { panic("null_fetch_syscall_args"); diff --git a/freebsd/sys/kern/kern_event.c b/freebsd/sys/kern/kern_event.c index 0a64adbe..2428182c 100644 --- a/freebsd/sys/kern/kern_event.c +++ b/freebsd/sys/kern/kern_event.c @@ -31,6 +31,7 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include <rtems/bsd/local/opt_compat.h> #include <rtems/bsd/local/opt_ktrace.h> #include <rtems/bsd/local/opt_kqueue.h> @@ -119,6 +120,10 @@ static int kqueue_scan(struct kqueue *kq, int maxevents, static void kqueue_wakeup(struct kqueue *kq); static struct filterops *kqueue_fo_find(int filt); static void kqueue_fo_release(int filt); +struct g_kevent_args; +static int kern_kevent_generic(struct thread *td, + struct g_kevent_args *uap, + struct kevent_copyops *k_ops); #ifndef __rtems__ static fo_rdwr_t kqueue_read; @@ -640,12 +645,13 @@ knote_fork(struct knlist *list, int pid) * interval timer support code. */ -#define NOTE_TIMER_PRECMASK (NOTE_SECONDS|NOTE_MSECONDS|NOTE_USECONDS| \ - NOTE_NSECONDS) +#define NOTE_TIMER_PRECMASK \ + (NOTE_SECONDS | NOTE_MSECONDS | NOTE_USECONDS | NOTE_NSECONDS) static sbintime_t timer2sbintime(intptr_t data, int flags) { + int64_t secs; /* * Macros for converting to the fractional second portion of an @@ -664,27 +670,27 @@ timer2sbintime(intptr_t data, int flags) case NOTE_MSECONDS: /* FALLTHROUGH */ case 0: if (data >= 1000) { - int64_t secs = data / 1000; + secs = data / 1000; #ifdef __LP64__ if (secs > (SBT_MAX / SBT_1S)) return (SBT_MAX); #endif return (secs << 32 | MS_TO_SBT(data % 1000)); } - return MS_TO_SBT(data); + return (MS_TO_SBT(data)); case NOTE_USECONDS: if (data >= 1000000) { - int64_t secs = data / 1000000; + secs = data / 1000000; #ifdef __LP64__ if (secs > (SBT_MAX / SBT_1S)) return (SBT_MAX); #endif return (secs << 32 | US_TO_SBT(data % 1000000)); } - return US_TO_SBT(data); + return (US_TO_SBT(data)); case NOTE_NSECONDS: if (data >= 1000000000) { - int64_t secs = data / 1000000000; + secs = data / 1000000000; #ifdef __LP64__ if (secs > (SBT_MAX / SBT_1S)) return (SBT_MAX); @@ -701,7 +707,7 @@ timer2sbintime(intptr_t data, int flags) struct kq_timer_cb_data { struct callout c; sbintime_t next; /* next timer event fires at */ - sbintime_t to; /* precalculated timer period */ + sbintime_t to; /* precalculated timer period, 0 for abs */ }; static void @@ -716,8 +722,9 @@ filt_timerexpire(void *knx) if ((kn->kn_flags & EV_ONESHOT) != 0) return; - kc = kn->kn_ptr.p_v; + if (kc->to == 0) + return; kc->next += kc->to; callout_reset_sbt_on(&kc->c, kc->next, 0, filt_timerexpire, kn, PCPU_GET(cpuid), C_ABSOLUTE); @@ -730,7 +737,8 @@ static int filt_timerattach(struct knote *kn) { struct kq_timer_cb_data *kc; - sbintime_t to; + struct bintime bt; + sbintime_t to, sbt; unsigned int ncallouts; if (kn->kn_sdata < 0) @@ -738,10 +746,15 @@ filt_timerattach(struct knote *kn) if (kn->kn_sdata == 0 && (kn->kn_flags & EV_ONESHOT) == 0) kn->kn_sdata = 1; /* Only precision unit are supported in flags so far */ - if ((kn->kn_sfflags & ~NOTE_TIMER_PRECMASK) != 0) + if ((kn->kn_sfflags & ~(NOTE_TIMER_PRECMASK | NOTE_ABSTIME)) != 0) return (EINVAL); to = timer2sbintime(kn->kn_sdata, kn->kn_sfflags); + if ((kn->kn_sfflags & NOTE_ABSTIME) != 0) { + getboottimebin(&bt); + sbt = bttosbt(bt); + to -= sbt; + } if (to < 0) return (EINVAL); @@ -751,12 +764,18 @@ filt_timerattach(struct knote *kn) return (ENOMEM); } while (!atomic_cmpset_int(&kq_ncallouts, ncallouts, ncallouts + 1)); - kn->kn_flags |= EV_CLEAR; /* automatically set */ + if ((kn->kn_sfflags & NOTE_ABSTIME) == 0) + kn->kn_flags |= EV_CLEAR; /* automatically set */ kn->kn_status &= ~KN_DETACHED; /* knlist_add clears it */ kn->kn_ptr.p_v = kc = malloc(sizeof(*kc), M_KQUEUE, M_WAITOK); callout_init(&kc->c, 1); - kc->next = to + sbinuptime(); - kc->to = to; + if ((kn->kn_sfflags & NOTE_ABSTIME) != 0) { + kc->next = to; + kc->to = 0; + } else { + kc->next = to + sbinuptime(); + kc->to = to; + } callout_reset_sbt_on(&kc->c, kc->next, 0, filt_timerexpire, kn, PCPU_GET(cpuid), C_ABSOLUTE); @@ -970,25 +989,24 @@ kqueue(void) #ifdef KTRACE static size_t -kev_iovlen(int n, u_int kgio) +kev_iovlen(int n, u_int kgio, size_t kevent_size) { - if (n < 0 || n >= kgio / sizeof(struct kevent)) + if (n < 0 || n >= kgio / kevent_size) return (kgio); - return (n * sizeof(struct kevent)); + return (n * kevent_size); } #endif -#ifndef _SYS_SYSPROTO_H_ -struct kevent_args { +struct g_kevent_args { int fd; - const struct kevent *changelist; + void *changelist; int nchanges; - struct kevent *eventlist; + void *eventlist; int nevents; const struct timespec *timeout; }; -#endif + #ifdef __rtems__ static int kern_kevent(struct thread *td, int fd, int nchanges, int nevents, struct kevent_copyops *k_ops, const struct timespec *timeout); @@ -1001,12 +1019,29 @@ static int sys_kevent(struct thread *td, struct kevent_args *uap) { - struct timespec ts, *tsp; struct kevent_copyops k_ops = { .arg = uap, .k_copyout = kevent_copyout, .k_copyin = kevent_copyin, + .kevent_size = sizeof(struct kevent), }; + struct g_kevent_args gk_args = { + .fd = uap->fd, + .changelist = uap->changelist, + .nchanges = uap->nchanges, + .eventlist = uap->eventlist, + .nevents = uap->nevents, + .timeout = uap->timeout, + }; + + return (kern_kevent_generic(td, &gk_args, &k_ops)); +} + +static int +kern_kevent_generic(struct thread *td, struct g_kevent_args *uap, + struct kevent_copyops *k_ops) +{ + struct timespec ts, *tsp; int error; #ifdef KTRACE struct uio ktruio; @@ -1028,26 +1063,30 @@ sys_kevent(struct thread *td, struct kevent_args *uap) if (KTRPOINT(td, KTR_GENIO)) { kgio = ktr_geniosize; ktriov.iov_base = uap->changelist; - ktriov.iov_len = kev_iovlen(uap->nchanges, kgio); + ktriov.iov_len = kev_iovlen(uap->nchanges, kgio, + k_ops->kevent_size); ktruio = (struct uio){ .uio_iov = &ktriov, .uio_iovcnt = 1, .uio_segflg = UIO_USERSPACE, .uio_rw = UIO_READ, .uio_td = td }; ktruioin = cloneuio(&ktruio); ktriov.iov_base = uap->eventlist; - ktriov.iov_len = kev_iovlen(uap->nevents, kgio); - ktriov.iov_len = uap->nevents * sizeof(struct kevent); + ktriov.iov_len = kev_iovlen(uap->nevents, kgio, + k_ops->kevent_size); + ktriov.iov_len = uap->nevents * k_ops->kevent_size; ktruioout = cloneuio(&ktruio); } #endif error = kern_kevent(td, uap->fd, uap->nchanges, uap->nevents, - &k_ops, tsp); + k_ops, tsp); #ifdef KTRACE if (ktruioin != NULL) { - ktruioin->uio_resid = kev_iovlen(uap->nchanges, kgio); + ktruioin->uio_resid = kev_iovlen(uap->nchanges, kgio, + k_ops->kevent_size); ktrgenio(uap->fd, UIO_WRITE, ktruioin, 0); - ktruioout->uio_resid = kev_iovlen(td->td_retval[0], kgio); + ktruioout->uio_resid = kev_iovlen(td->td_retval[0], kgio, + k_ops->kevent_size); ktrgenio(uap->fd, UIO_READ, ktruioout, error); } #endif @@ -1123,6 +1162,94 @@ kevent_copyin(void *arg, struct kevent *kevp, int count) return (error); } +#ifdef COMPAT_FREEBSD11 +struct kevent_freebsd11 { + __uintptr_t ident; /* identifier for this event */ + short filter; /* filter for event */ + unsigned short flags; + unsigned int fflags; + __intptr_t data; + void *udata; /* opaque user data identifier */ +}; + +static int +kevent11_copyout(void *arg, struct kevent *kevp, int count) +{ + struct freebsd11_kevent_args *uap; + struct kevent_freebsd11 kev11; + int error, i; + + KASSERT(count <= KQ_NEVENTS, ("count (%d) > KQ_NEVENTS", count)); + uap = (struct freebsd11_kevent_args *)arg; + + for (i = 0; i < count; i++) { + kev11.ident = kevp->ident; + kev11.filter = kevp->filter; + kev11.flags = kevp->flags; + kev11.fflags = kevp->fflags; + kev11.data = kevp->data; + kev11.udata = kevp->udata; + error = copyout(&kev11, uap->eventlist, sizeof(kev11)); + if (error != 0) + break; + uap->eventlist++; + kevp++; + } + return (error); +} + +/* + * Copy 'count' items from the list pointed to by uap->changelist. + */ +static int +kevent11_copyin(void *arg, struct kevent *kevp, int count) +{ + struct freebsd11_kevent_args *uap; + struct kevent_freebsd11 kev11; + int error, i; + + KASSERT(count <= KQ_NEVENTS, ("count (%d) > KQ_NEVENTS", count)); + uap = (struct freebsd11_kevent_args *)arg; + + for (i = 0; i < count; i++) { + error = copyin(uap->changelist, &kev11, sizeof(kev11)); + if (error != 0) + break; + kevp->ident = kev11.ident; + kevp->filter = kev11.filter; + kevp->flags = kev11.flags; + kevp->fflags = kev11.fflags; + kevp->data = (uintptr_t)kev11.data; + kevp->udata = kev11.udata; + bzero(&kevp->ext, sizeof(kevp->ext)); + uap->changelist++; + kevp++; + } + return (error); +} + +int +freebsd11_kevent(struct thread *td, struct freebsd11_kevent_args *uap) +{ + struct kevent_copyops k_ops = { + .arg = uap, + .k_copyout = kevent11_copyout, + .k_copyin = kevent11_copyin, + .kevent_size = sizeof(struct kevent_freebsd11), + }; + struct g_kevent_args gk_args = { + .fd = uap->fd, + .changelist = uap->changelist, + .nchanges = uap->nchanges, + .eventlist = uap->eventlist, + .nevents = uap->nevents, + .timeout = uap->timeout, + }; + + return (kern_kevent_generic(td, &gk_args, &k_ops)); +} +#endif + int kern_kevent(struct thread *td, int fd, int nchanges, int nevents, struct kevent_copyops *k_ops, const struct timespec *timeout) diff --git a/freebsd/sys/kern/kern_linker.c b/freebsd/sys/kern/kern_linker.c index 214554d3..1c81a61c 100644 --- a/freebsd/sys/kern/kern_linker.c +++ b/freebsd/sys/kern/kern_linker.c @@ -1259,8 +1259,8 @@ kern_kldstat(struct thread *td, int fileid, struct kld_file_stat *stat) /* Version 1 fields: */ namelen = strlen(lf->filename) + 1; - if (namelen > MAXPATHLEN) - namelen = MAXPATHLEN; + if (namelen > sizeof(stat->name)) + namelen = sizeof(stat->name); bcopy(lf->filename, &stat->name[0], namelen); stat->refs = lf->refs; stat->id = lf->id; @@ -1268,8 +1268,8 @@ kern_kldstat(struct thread *td, int fileid, struct kld_file_stat *stat) stat->size = lf->size; /* Version 2 fields: */ namelen = strlen(lf->pathname) + 1; - if (namelen > MAXPATHLEN) - namelen = MAXPATHLEN; + if (namelen > sizeof(stat->pathname)) + namelen = sizeof(stat->pathname); bcopy(lf->pathname, &stat->pathname[0], namelen); sx_xunlock(&kld_sx); diff --git a/freebsd/sys/kern/kern_uuid.c b/freebsd/sys/kern/kern_uuid.c index b6a8915f..1ac19685 100644 --- a/freebsd/sys/kern/kern_uuid.c +++ b/freebsd/sys/kern/kern_uuid.c @@ -60,7 +60,7 @@ CTASSERT(sizeof(struct uuid) == 16); /* We use an alternative, more convenient representation in the generator. */ struct uuid_private { union { - uint64_t ll; /* internal. */ + uint64_t ll; /* internal, for uuid_last only */ struct { uint32_t low; uint16_t mid; @@ -428,3 +428,10 @@ parse_uuid(const char *str, struct uuid *uuid) (c[3] & 0xc0) != 0x80 && /* variant 1? */ (c[3] & 0xe0) != 0xc0) ? EINVAL : 0); /* variant 2? */ } + +int +uuidcmp(const struct uuid *uuid1, const struct uuid *uuid2) +{ + + return (memcmp(uuid1, uuid2, sizeof(struct uuid))); +} diff --git a/freebsd/sys/kern/subr_blist.c b/freebsd/sys/kern/subr_blist.c index 5af51dd4..c8e32c5b 100644 --- a/freebsd/sys/kern/subr_blist.c +++ b/freebsd/sys/kern/subr_blist.c @@ -30,18 +30,18 @@ * BLIST.C - Bitmap allocator/deallocator, using a radix tree with hinting * * This module implements a general bitmap allocator/deallocator. The - * allocator eats around 2 bits per 'block'. The module does not - * try to interpret the meaning of a 'block' other than to return + * allocator eats around 2 bits per 'block'. The module does not + * try to interpret the meaning of a 'block' other than to return * SWAPBLK_NONE on an allocation failure. * * A radix tree is used to maintain the bitmap. Two radix constants are * involved: One for the bitmaps contained in the leaf nodes (typically - * 32), and one for the meta nodes (typically 16). Both meta and leaf + * 64), and one for the meta nodes (typically 16). Both meta and leaf * nodes have a hint field. This field gives us a hint as to the largest * free contiguous range of blocks under the node. It may contain a - * value that is too high, but will never contain a value that is too + * value that is too high, but will never contain a value that is too * low. When the radix tree is searched, allocation failures in subtrees - * update the hint. + * update the hint. * * The radix tree also implements two collapsed states for meta nodes: * the ALL-ALLOCATED state and the ALL-FREE state. If a meta node is @@ -51,7 +51,7 @@ * * The hinting greatly increases code efficiency for allocations while * the general radix structure optimizes both allocations and frees. The - * radix tree should be able to operate well no matter how much + * radix tree should be able to operate well no matter how much * fragmentation there is and no matter how large a bitmap is used. * * The blist code wires all necessary memory at creation time. Neither @@ -63,18 +63,18 @@ * linear array. Each meta node is immediately followed (laid out * sequentially in memory) by BLIST_META_RADIX lower level nodes. This * is a recursive structure but one that can be easily scanned through - * a very simple 'skip' calculation. In order to support large radixes, - * portions of the tree may reside outside our memory allocation. We - * handle this with an early-termination optimization (when bighint is - * set to -1) on the scan. The memory allocation is only large enough + * a very simple 'skip' calculation. In order to support large radixes, + * portions of the tree may reside outside our memory allocation. We + * handle this with an early-termination optimization (when bighint is + * set to -1) on the scan. The memory allocation is only large enough * to cover the number of blocks requested at creation time even if it * must be encompassed in larger root-node radix. * - * NOTE: the allocator cannot currently allocate more than - * BLIST_BMAP_RADIX blocks per call. It will panic with 'allocation too - * large' if you try. This is an area that could use improvement. The - * radix is large enough that this restriction does not effect the swap - * system, though. Currently only the allocation code is effected by + * NOTE: the allocator cannot currently allocate more than + * BLIST_BMAP_RADIX blocks per call. It will panic with 'allocation too + * large' if you try. This is an area that could use improvement. The + * radix is large enough that this restriction does not effect the swap + * system, though. Currently only the allocation code is affected by * this algorithmic unfeature. The freeing code can handle arbitrary * ranges. * @@ -93,7 +93,7 @@ __FBSDID("$FreeBSD$"); #include <sys/blist.h> #include <sys/malloc.h> #include <sys/proc.h> -#include <sys/mutex.h> +#include <sys/mutex.h> #else @@ -101,19 +101,18 @@ __FBSDID("$FreeBSD$"); #define BLIST_DEBUG #endif -#define SWAPBLK_NONE ((daddr_t)-1) - #include <sys/types.h> +#include <sys/malloc.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <stdarg.h> +#include <stdbool.h> +#define bitcount64(x) __bitcount64((uint64_t)(x)) #define malloc(a,b,c) calloc(a, 1) #define free(a,b) free(a) -typedef unsigned int u_daddr_t; - #include <sys/blist.h> void panic(const char *ctl, ...); @@ -123,23 +122,23 @@ void panic(const char *ctl, ...); /* * static support functions */ - -static daddr_t blst_leaf_alloc(blmeta_t *scan, daddr_t blk, int count); -static daddr_t blst_meta_alloc(blmeta_t *scan, daddr_t blk, - daddr_t count, daddr_t radix, int skip); +static daddr_t blst_leaf_alloc(blmeta_t *scan, daddr_t blk, int count, + daddr_t cursor); +static daddr_t blst_meta_alloc(blmeta_t *scan, daddr_t blk, daddr_t count, + daddr_t radix, daddr_t skip, daddr_t cursor); static void blst_leaf_free(blmeta_t *scan, daddr_t relblk, int count); -static void blst_meta_free(blmeta_t *scan, daddr_t freeBlk, daddr_t count, - daddr_t radix, int skip, daddr_t blk); -static void blst_copy(blmeta_t *scan, daddr_t blk, daddr_t radix, - daddr_t skip, blist_t dest, daddr_t count); -static int blst_leaf_fill(blmeta_t *scan, daddr_t blk, int count); -static int blst_meta_fill(blmeta_t *scan, daddr_t allocBlk, daddr_t count, - daddr_t radix, int skip, daddr_t blk); -static daddr_t blst_radix_init(blmeta_t *scan, daddr_t radix, - int skip, daddr_t count); +static void blst_meta_free(blmeta_t *scan, daddr_t freeBlk, daddr_t count, + daddr_t radix, daddr_t skip, daddr_t blk); +static void blst_copy(blmeta_t *scan, daddr_t blk, daddr_t radix, + daddr_t skip, blist_t dest, daddr_t count); +static daddr_t blst_leaf_fill(blmeta_t *scan, daddr_t blk, int count); +static daddr_t blst_meta_fill(blmeta_t *scan, daddr_t allocBlk, daddr_t count, + daddr_t radix, daddr_t skip, daddr_t blk); +static daddr_t blst_radix_init(blmeta_t *scan, daddr_t radix, daddr_t skip, + daddr_t count); #ifndef _KERNEL -static void blst_radix_print(blmeta_t *scan, daddr_t blk, - daddr_t radix, int skip, int tab); +static void blst_radix_print(blmeta_t *scan, daddr_t blk, daddr_t radix, + daddr_t skip, int tab); #endif #ifdef _KERNEL @@ -153,35 +152,40 @@ static MALLOC_DEFINE(M_SWAP, "SWAP", "Swap space"); * blocks - must be greater than 0 * flags - malloc flags * - * The smallest blist consists of a single leaf node capable of + * The smallest blist consists of a single leaf node capable of * managing BLIST_BMAP_RADIX blocks. */ - -blist_t +blist_t blist_create(daddr_t blocks, int flags) { blist_t bl; - int radix; - int skip = 0; + daddr_t nodes, radix, skip; /* * Calculate radix and skip field used for scanning. */ radix = BLIST_BMAP_RADIX; - + skip = 0; while (radix < blocks) { radix *= BLIST_META_RADIX; skip = (skip + 1) * BLIST_META_RADIX; } + nodes = 1 + blst_radix_init(NULL, radix, skip, blocks); - bl = malloc(sizeof(struct blist), M_SWAP, flags | M_ZERO); + bl = malloc(sizeof(struct blist), M_SWAP, flags); + if (bl == NULL) + return (NULL); bl->bl_blocks = blocks; bl->bl_radix = radix; bl->bl_skip = skip; - bl->bl_rootblks = 1 + - blst_radix_init(NULL, bl->bl_radix, bl->bl_skip, blocks); - bl->bl_root = malloc(sizeof(blmeta_t) * bl->bl_rootblks, M_SWAP, flags); + bl->bl_cursor = 0; + bl->bl_root = malloc(nodes * sizeof(blmeta_t), M_SWAP, flags); + if (bl->bl_root == NULL) { + free(bl, M_SWAP); + return (NULL); + } + blst_radix_init(bl->bl_root, radix, skip, blocks); #if defined(BLIST_DEBUG) printf( @@ -189,17 +193,16 @@ blist_create(daddr_t blocks, int flags) ", requiring %lldK of ram\n", (long long)bl->bl_blocks, (long long)bl->bl_blocks * 4 / 1024, - (long long)(bl->bl_rootblks * sizeof(blmeta_t) + 1023) / 1024 + (long long)(nodes * sizeof(blmeta_t) + 1023) / 1024 ); printf("BLIST raw radix tree contains %lld records\n", - (long long)bl->bl_rootblks); + (long long)nodes); #endif - blst_radix_init(bl->bl_root, bl->bl_radix, bl->bl_skip, blocks); - return(bl); + return (bl); } -void +void blist_destroy(blist_t bl) { free(bl->bl_root, M_SWAP); @@ -207,25 +210,44 @@ blist_destroy(blist_t bl) } /* - * blist_alloc() - reserve space in the block bitmap. Return the base + * blist_alloc() - reserve space in the block bitmap. Return the base * of a contiguous region or SWAPBLK_NONE if space could * not be allocated. */ - -daddr_t +daddr_t blist_alloc(blist_t bl, daddr_t count) { - daddr_t blk = SWAPBLK_NONE; + daddr_t blk; - if (bl) { - if (bl->bl_radix == BLIST_BMAP_RADIX) - blk = blst_leaf_alloc(bl->bl_root, 0, count); - else - blk = blst_meta_alloc(bl->bl_root, 0, count, bl->bl_radix, bl->bl_skip); - if (blk != SWAPBLK_NONE) - bl->bl_free -= count; + /* + * This loop iterates at most twice. An allocation failure in the + * first iteration leads to a second iteration only if the cursor was + * non-zero. When the cursor is zero, an allocation failure will + * reduce the hint, stopping further iterations. + */ + while (count <= bl->bl_root->bm_bighint) { + blk = blst_meta_alloc(bl->bl_root, 0, count, bl->bl_radix, + bl->bl_skip, bl->bl_cursor); + if (blk != SWAPBLK_NONE) { + bl->bl_cursor = blk + count; + return (blk); + } else if (bl->bl_cursor != 0) + bl->bl_cursor = 0; } - return(blk); + return (SWAPBLK_NONE); +} + +/* + * blist_avail() - return the number of free blocks. + */ +daddr_t +blist_avail(blist_t bl) +{ + + if (bl->bl_radix == BLIST_BMAP_RADIX) + return (bitcount64(bl->bl_root->u.bmu_bitmap)); + else + return (bl->bl_root->u.bmu_avail); } /* @@ -233,17 +255,11 @@ blist_alloc(blist_t bl, daddr_t count) * of a contiguous region. Panic if an inconsistancy is * found. */ - -void +void blist_free(blist_t bl, daddr_t blkno, daddr_t count) { - if (bl) { - if (bl->bl_radix == BLIST_BMAP_RADIX) - blst_leaf_free(bl->bl_root, blkno, count); - else - blst_meta_free(bl->bl_root, blkno, count, bl->bl_radix, bl->bl_skip, 0); - bl->bl_free += count; - } + + blst_meta_free(bl->bl_root, blkno, count, bl->bl_radix, bl->bl_skip, 0); } /* @@ -252,22 +268,12 @@ blist_free(blist_t bl, daddr_t blkno, daddr_t count) * existing allocations. Return the number of blocks * actually filled that were free before the call. */ - -int +daddr_t blist_fill(blist_t bl, daddr_t blkno, daddr_t count) { - int filled; - if (bl) { - if (bl->bl_radix == BLIST_BMAP_RADIX) - filled = blst_leaf_fill(bl->bl_root, blkno, count); - else - filled = blst_meta_fill(bl->bl_root, blkno, count, - bl->bl_radix, bl->bl_skip, 0); - bl->bl_free -= filled; - return filled; - } else - return 0; + return (blst_meta_fill(bl->bl_root, blkno, count, bl->bl_radix, + bl->bl_skip, 0)); } /* @@ -277,7 +283,6 @@ blist_fill(blist_t bl, daddr_t blkno, daddr_t count) * one. When extending the tree you can specify whether * the new blocks are to left allocated or freed. */ - void blist_resize(blist_t *pbl, daddr_t count, int freenew, int flags) { @@ -303,7 +308,6 @@ blist_resize(blist_t *pbl, daddr_t count, int freenew, int flags) /* * blist_print() - dump radix tree */ - void blist_print(blist_t bl) { @@ -318,7 +322,7 @@ blist_print(blist_t bl) * ALLOCATION SUPPORT FUNCTIONS * ************************************************************************ * - * These support functions do all the actual work. They may seem + * These support functions do all the actual work. They may seem * rather longish, but that's because I've commented them up. The * actual code is straight forward. * @@ -327,77 +331,91 @@ blist_print(blist_t bl) /* * blist_leaf_alloc() - allocate at a leaf in the radix tree (a bitmap). * - * This is the core of the allocator and is optimized for the 1 block - * and the BLIST_BMAP_RADIX block allocation cases. Other cases are - * somewhat slower. The 1 block allocation case is log2 and extremely - * quick. + * This is the core of the allocator and is optimized for the + * BLIST_BMAP_RADIX block allocation case. Otherwise, execution + * time is proportional to log2(count) + log2(BLIST_BMAP_RADIX). */ - static daddr_t -blst_leaf_alloc( - blmeta_t *scan, - daddr_t blk, - int count -) { - u_daddr_t orig = scan->u.bmu_bitmap; - - if (orig == 0) { +blst_leaf_alloc(blmeta_t *scan, daddr_t blk, int count, daddr_t cursor) +{ + u_daddr_t mask; + int count1, hi, lo, mid, num_shifts, range1, range_ext; + + if (count == BLIST_BMAP_RADIX) { /* - * Optimize bitmap all-allocated case. Also, count = 1 - * case assumes at least 1 bit is free in the bitmap, so - * we have to take care of this case here. + * Optimize allocation of BLIST_BMAP_RADIX bits. If this wasn't + * a special case, then forming the final value of 'mask' below + * would require special handling to avoid an invalid left shift + * when count equals the number of bits in mask. */ + if (~scan->u.bmu_bitmap != 0) { + scan->bm_bighint = BLIST_BMAP_RADIX - 1; + return (SWAPBLK_NONE); + } + if (cursor != blk) + return (SWAPBLK_NONE); + scan->u.bmu_bitmap = 0; scan->bm_bighint = 0; - return(SWAPBLK_NONE); + return (blk); } - if (count == 1) { + range1 = 0; + count1 = count - 1; + num_shifts = fls(count1); + mask = scan->u.bmu_bitmap; + while (mask != 0 && num_shifts > 0) { /* - * Optimized code to allocate one bit out of the bitmap + * If bit i is set in mask, then bits in [i, i+range1] are set + * in scan->u.bmu_bitmap. The value of range1 is equal to + * count1 >> num_shifts. Grow range and reduce num_shifts to 0, + * while preserving these invariants. The updates to mask leave + * fewer bits set, but each bit that remains set represents a + * longer string of consecutive bits set in scan->u.bmu_bitmap. */ - u_daddr_t mask; - int j = BLIST_BMAP_RADIX/2; - int r = 0; - - mask = (u_daddr_t)-1 >> (BLIST_BMAP_RADIX/2); - - while (j) { - if ((orig & mask) == 0) { - r += j; - orig >>= j; - } - j >>= 1; - mask >>= j; - } - scan->u.bmu_bitmap &= ~(1 << r); - return(blk + r); + num_shifts--; + range_ext = range1 + ((count1 >> num_shifts) & 1); + mask &= mask >> range_ext; + range1 += range_ext; } - if (count <= BLIST_BMAP_RADIX) { + if (mask == 0) { /* - * non-optimized code to allocate N bits out of the bitmap. - * The more bits, the faster the code runs. It will run - * the slowest allocating 2 bits, but since there aren't any - * memory ops in the core loop (or shouldn't be, anyway), - * you probably won't notice the difference. + * Update bighint. There is no allocation bigger than range1 + * available in this leaf. */ - int j; - int n = BLIST_BMAP_RADIX - count; - u_daddr_t mask; + scan->bm_bighint = range1; + return (SWAPBLK_NONE); + } - mask = (u_daddr_t)-1 >> n; + /* + * Discard any candidates that appear before the cursor. + */ + lo = cursor - blk; + mask &= ~(u_daddr_t)0 << lo; - for (j = 0; j <= n; ++j) { - if ((orig & mask) == mask) { - scan->u.bmu_bitmap &= ~mask; - return(blk + j); - } - mask = (mask << 1); - } + if (mask == 0) + return (SWAPBLK_NONE); + + /* + * The least significant set bit in mask marks the start of the first + * available range of sufficient size. Clear all the bits but that one, + * and then perform a binary search to find its position. + */ + mask &= -mask; + hi = BLIST_BMAP_RADIX - count1; + while (lo + 1 < hi) { + mid = (lo + hi) >> 1; + if ((mask >> mid) != 0) + lo = mid; + else + hi = mid; } + /* - * We couldn't allocate count in this subtree, update bighint. + * Set in mask exactly the bits being allocated, and clear them from + * the set of available bits. */ - scan->bm_bighint = count - 1; - return(SWAPBLK_NONE); + mask = (mask << count) - mask; + scan->u.bmu_bitmap &= ~mask; + return (blk + lo); } /* @@ -408,76 +426,75 @@ blst_leaf_alloc( * calls that hit this node. We have to check for our collapse cases * and we have a few optimizations strewn in as well. */ - static daddr_t -blst_meta_alloc( - blmeta_t *scan, - daddr_t blk, - daddr_t count, - daddr_t radix, - int skip -) { - int i; - int next_skip = ((u_int)skip / BLIST_META_RADIX); +blst_meta_alloc(blmeta_t *scan, daddr_t blk, daddr_t count, daddr_t radix, + daddr_t skip, daddr_t cursor) +{ + daddr_t i, next_skip, r; + int child; + bool scan_from_start; - if (scan->u.bmu_avail == 0) { + if (radix == BLIST_BMAP_RADIX) + return (blst_leaf_alloc(scan, blk, count, cursor)); + if (scan->u.bmu_avail < count) { /* - * ALL-ALLOCATED special case + * The meta node's hint must be too large if the allocation + * exceeds the number of free blocks. Reduce the hint, and + * return failure. */ - scan->bm_bighint = count; - return(SWAPBLK_NONE); + scan->bm_bighint = scan->u.bmu_avail; + return (SWAPBLK_NONE); } + next_skip = skip / BLIST_META_RADIX; + /* + * An ALL-FREE meta node requires special handling before allocating + * any of its blocks. + */ if (scan->u.bmu_avail == radix) { radix /= BLIST_META_RADIX; /* - * ALL-FREE special case, initialize uninitialize - * sublevel. + * Reinitialize each of the meta node's children. An ALL-FREE + * meta node cannot have a terminator in any subtree. */ for (i = 1; i <= skip; i += next_skip) { - if (scan[i].bm_bighint == (daddr_t)-1) - break; - if (next_skip == 1) { + if (next_skip == 1) scan[i].u.bmu_bitmap = (u_daddr_t)-1; - scan[i].bm_bighint = BLIST_BMAP_RADIX; - } else { - scan[i].bm_bighint = radix; + else scan[i].u.bmu_avail = radix; - } + scan[i].bm_bighint = radix; } } else { radix /= BLIST_META_RADIX; } - for (i = 1; i <= skip; i += next_skip) { + if (count > radix) { + /* + * The allocation exceeds the number of blocks that are + * managed by a subtree of this meta node. + */ + panic("allocation too large"); + } + scan_from_start = cursor == blk; + child = (cursor - blk) / radix; + blk += child * radix; + for (i = 1 + child * next_skip; i <= skip; i += next_skip) { if (count <= scan[i].bm_bighint) { /* - * count fits in object + * The allocation might fit in the i'th subtree. */ - daddr_t r; - if (next_skip == 1) { - r = blst_leaf_alloc(&scan[i], blk, count); - } else { - r = blst_meta_alloc(&scan[i], blk, count, radix, next_skip - 1); - } + r = blst_meta_alloc(&scan[i], blk, count, radix, + next_skip - 1, cursor > blk ? cursor : blk); if (r != SWAPBLK_NONE) { scan->u.bmu_avail -= count; - if (scan->bm_bighint > scan->u.bmu_avail) - scan->bm_bighint = scan->u.bmu_avail; - return(r); + return (r); } } else if (scan[i].bm_bighint == (daddr_t)-1) { /* * Terminator */ break; - } else if (count > radix) { - /* - * count does not fit in object even if it were - * complete free. - */ - panic("blist_meta_alloc: allocation too large"); } blk += radix; } @@ -485,22 +502,19 @@ blst_meta_alloc( /* * We couldn't allocate count in this subtree, update bighint. */ - if (scan->bm_bighint >= count) + if (scan_from_start && scan->bm_bighint >= count) scan->bm_bighint = count - 1; - return(SWAPBLK_NONE); + + return (SWAPBLK_NONE); } /* * BLST_LEAF_FREE() - free allocated block from leaf bitmap * */ - static void -blst_leaf_free( - blmeta_t *scan, - daddr_t blk, - int count -) { +blst_leaf_free(blmeta_t *scan, daddr_t blk, int count) +{ /* * free some data in this bitmap * @@ -521,7 +535,7 @@ blst_leaf_free( /* * We could probably do a better job here. We are required to make - * bighint at least as large as the biggest contiguous block of + * bighint at least as large as the biggest contiguous block of * data. If we just shoehorn it, a little extra overhead will * be incured on the next allocation (but only that one typically). */ @@ -538,25 +552,18 @@ blst_leaf_free( * range whereas the allocation code cannot allocate an arbitrary * range). */ +static void +blst_meta_free(blmeta_t *scan, daddr_t freeBlk, daddr_t count, daddr_t radix, + daddr_t skip, daddr_t blk) +{ + daddr_t i, next_skip, v; + int child; -static void -blst_meta_free( - blmeta_t *scan, - daddr_t freeBlk, - daddr_t count, - daddr_t radix, - int skip, - daddr_t blk -) { - int i; - int next_skip = ((u_int)skip / BLIST_META_RADIX); - -#if 0 - printf("free (%llx,%lld) FROM (%llx,%lld)\n", - (long long)freeBlk, (long long)count, - (long long)blk, (long long)radix - ); -#endif + if (scan->bm_bighint == (daddr_t)-1) + panic("freeing invalid range"); + if (radix == BLIST_BMAP_RADIX) + return (blst_leaf_free(scan, freeBlk, count)); + next_skip = skip / BLIST_META_RADIX; if (scan->u.bmu_avail == 0) { /* @@ -601,27 +608,16 @@ blst_meta_free( radix /= BLIST_META_RADIX; - i = (freeBlk - blk) / radix; - blk += i * radix; - i = i * next_skip + 1; - + child = (freeBlk - blk) / radix; + blk += child * radix; + i = 1 + child * next_skip; while (i <= skip && blk < freeBlk + count) { - daddr_t v; - v = blk + radix - freeBlk; if (v > count) v = count; - - if (scan->bm_bighint == (daddr_t)-1) - panic("blst_meta_free: freeing unexpected range"); - - if (next_skip == 1) { - blst_leaf_free(&scan[i], freeBlk, v); - } else { - blst_meta_free(&scan[i], freeBlk, v, radix, next_skip - 1, blk); - } + blst_meta_free(&scan[i], freeBlk, v, radix, next_skip - 1, blk); if (scan->bm_bighint < scan[i].bm_bighint) - scan->bm_bighint = scan[i].bm_bighint; + scan->bm_bighint = scan[i].bm_bighint; count -= v; freeBlk += v; blk += radix; @@ -635,17 +631,11 @@ blst_meta_free( * Locates free space in the source tree and frees it in the destination * tree. The space may not already be free in the destination. */ - -static void blst_copy( - blmeta_t *scan, - daddr_t blk, - daddr_t radix, - daddr_t skip, - blist_t dest, - daddr_t count -) { - int next_skip; - int i; +static void +blst_copy(blmeta_t *scan, daddr_t blk, daddr_t radix, daddr_t skip, + blist_t dest, daddr_t count) +{ + daddr_t i, next_skip; /* * Leaf node @@ -660,7 +650,7 @@ static void blst_copy( int i; for (i = 0; i < BLIST_BMAP_RADIX && i < count; ++i) { - if (v & (1 << i)) + if (v & ((u_daddr_t)1 << i)) blist_free(dest, blk + i, 1); } } @@ -676,7 +666,7 @@ static void blst_copy( * Source all allocated, leave dest allocated */ return; - } + } if (scan->u.bmu_avail == radix) { /* * Source all free, free entire dest @@ -690,32 +680,20 @@ static void blst_copy( radix /= BLIST_META_RADIX; - next_skip = ((u_int)skip / BLIST_META_RADIX); + next_skip = skip / BLIST_META_RADIX; for (i = 1; count && i <= skip; i += next_skip) { if (scan[i].bm_bighint == (daddr_t)-1) break; if (count >= radix) { - blst_copy( - &scan[i], - blk, - radix, - next_skip - 1, - dest, - radix - ); + blst_copy(&scan[i], blk, radix, next_skip - 1, dest, + radix); count -= radix; } else { if (count) { - blst_copy( - &scan[i], - blk, - radix, - next_skip - 1, - dest, - count - ); + blst_copy(&scan[i], blk, radix, next_skip - 1, + dest, count); } count = 0; } @@ -730,24 +708,21 @@ static void blst_copy( * regardless of any existing allocations in that range. Returns * the number of blocks allocated by the call. */ - -static int +static daddr_t blst_leaf_fill(blmeta_t *scan, daddr_t blk, int count) { int n = blk & (BLIST_BMAP_RADIX - 1); - int nblks; - u_daddr_t mask, bitmap; + daddr_t nblks; + u_daddr_t mask; mask = ((u_daddr_t)-1 << n) & ((u_daddr_t)-1 >> (BLIST_BMAP_RADIX - count - n)); - /* Count the number of blocks we're about to allocate */ - bitmap = scan->u.bmu_bitmap & mask; - for (nblks = 0; bitmap != 0; nblks++) - bitmap &= bitmap - 1; + /* Count the number of blocks that we are allocating. */ + nblks = bitcount64(scan->u.bmu_bitmap & mask); scan->u.bmu_bitmap &= ~mask; - return nblks; + return (nblks); } /* @@ -758,80 +733,74 @@ blst_leaf_fill(blmeta_t *scan, daddr_t blk, int count) * range must be within the extent of this node. Returns the * number of blocks allocated by the call. */ -static int -blst_meta_fill( - blmeta_t *scan, - daddr_t allocBlk, - daddr_t count, - daddr_t radix, - int skip, - daddr_t blk -) { - int i; - int next_skip = ((u_int)skip / BLIST_META_RADIX); - int nblks = 0; +static daddr_t +blst_meta_fill(blmeta_t *scan, daddr_t allocBlk, daddr_t count, daddr_t radix, + daddr_t skip, daddr_t blk) +{ + daddr_t i, nblks, next_skip, v; + int child; + if (scan->bm_bighint == (daddr_t)-1) + panic("filling invalid range"); + if (count > radix) { + /* + * The allocation exceeds the number of blocks that are + * managed by this node. + */ + panic("fill too large"); + } + if (radix == BLIST_BMAP_RADIX) + return (blst_leaf_fill(scan, allocBlk, count)); if (count == radix || scan->u.bmu_avail == 0) { /* * ALL-ALLOCATED special case */ nblks = scan->u.bmu_avail; scan->u.bmu_avail = 0; - scan->bm_bighint = count; - return nblks; + scan->bm_bighint = 0; + return (nblks); } + next_skip = skip / BLIST_META_RADIX; + /* + * An ALL-FREE meta node requires special handling before allocating + * any of its blocks. + */ if (scan->u.bmu_avail == radix) { radix /= BLIST_META_RADIX; /* - * ALL-FREE special case, initialize sublevel + * Reinitialize each of the meta node's children. An ALL-FREE + * meta node cannot have a terminator in any subtree. */ for (i = 1; i <= skip; i += next_skip) { - if (scan[i].bm_bighint == (daddr_t)-1) - break; - if (next_skip == 1) { + if (next_skip == 1) scan[i].u.bmu_bitmap = (u_daddr_t)-1; - scan[i].bm_bighint = BLIST_BMAP_RADIX; - } else { - scan[i].bm_bighint = radix; + else scan[i].u.bmu_avail = radix; - } + scan[i].bm_bighint = radix; } } else { radix /= BLIST_META_RADIX; } - if (count > radix) - panic("blist_meta_fill: allocation too large"); - - i = (allocBlk - blk) / radix; - blk += i * radix; - i = i * next_skip + 1; - + nblks = 0; + child = (allocBlk - blk) / radix; + blk += child * radix; + i = 1 + child * next_skip; while (i <= skip && blk < allocBlk + count) { - daddr_t v; - v = blk + radix - allocBlk; if (v > count) v = count; - - if (scan->bm_bighint == (daddr_t)-1) - panic("blst_meta_fill: filling unexpected range"); - - if (next_skip == 1) { - nblks += blst_leaf_fill(&scan[i], allocBlk, v); - } else { - nblks += blst_meta_fill(&scan[i], allocBlk, v, - radix, next_skip - 1, blk); - } + nblks += blst_meta_fill(&scan[i], allocBlk, v, radix, + next_skip - 1, blk); count -= v; allocBlk += v; blk += radix; i += next_skip; } scan->u.bmu_avail -= nblks; - return nblks; + return (nblks); } /* @@ -842,13 +811,12 @@ blst_meta_fill( * be considerably less than the calculated radix due to the large * RADIX values we use. */ - -static daddr_t -blst_radix_init(blmeta_t *scan, daddr_t radix, int skip, daddr_t count) +static daddr_t +blst_radix_init(blmeta_t *scan, daddr_t radix, daddr_t skip, daddr_t count) { - int i; - int next_skip; - daddr_t memindex = 0; + daddr_t i, memindex, next_skip; + + memindex = 0; /* * Leaf node @@ -859,7 +827,7 @@ blst_radix_init(blmeta_t *scan, daddr_t radix, int skip, daddr_t count) scan->bm_bighint = 0; scan->u.bmu_bitmap = 0; } - return(memindex); + return (memindex); } /* @@ -874,30 +842,24 @@ blst_radix_init(blmeta_t *scan, daddr_t radix, int skip, daddr_t count) } radix /= BLIST_META_RADIX; - next_skip = ((u_int)skip / BLIST_META_RADIX); + next_skip = skip / BLIST_META_RADIX; for (i = 1; i <= skip; i += next_skip) { if (count >= radix) { /* * Allocate the entire object */ - memindex = i + blst_radix_init( - ((scan) ? &scan[i] : NULL), - radix, - next_skip - 1, - radix - ); + memindex = i + + blst_radix_init(((scan) ? &scan[i] : NULL), radix, + next_skip - 1, radix); count -= radix; } else if (count > 0) { /* * Allocate a partial object */ - memindex = i + blst_radix_init( - ((scan) ? &scan[i] : NULL), - radix, - next_skip - 1, - count - ); + memindex = i + + blst_radix_init(((scan) ? &scan[i] : NULL), radix, + next_skip - 1, count); count = 0; } else { /* @@ -910,21 +872,20 @@ blst_radix_init(blmeta_t *scan, daddr_t radix, int skip, daddr_t count) } if (memindex < i) memindex = i; - return(memindex); + return (memindex); } #ifdef BLIST_DEBUG -static void -blst_radix_print(blmeta_t *scan, daddr_t blk, daddr_t radix, int skip, int tab) +static void +blst_radix_print(blmeta_t *scan, daddr_t blk, daddr_t radix, daddr_t skip, + int tab) { - int i; - int next_skip; - int lastState = 0; + daddr_t i, next_skip; if (radix == BLIST_BMAP_RADIX) { printf( - "%*.*s(%08llx,%lld): bitmap %08llx big=%lld\n", + "%*.*s(%08llx,%lld): bitmap %016llx big=%lld\n", tab, tab, "", (long long)blk, (long long)radix, (long long)scan->u.bmu_bitmap, @@ -962,7 +923,7 @@ blst_radix_print(blmeta_t *scan, daddr_t blk, daddr_t radix, int skip, int tab) ); radix /= BLIST_META_RADIX; - next_skip = ((u_int)skip / BLIST_META_RADIX); + next_skip = skip / BLIST_META_RADIX; tab += 4; for (i = 1; i <= skip; i += next_skip) { @@ -972,16 +933,9 @@ blst_radix_print(blmeta_t *scan, daddr_t blk, daddr_t radix, int skip, int tab) tab, tab, "", (long long)blk, (long long)radix ); - lastState = 0; break; } - blst_radix_print( - &scan[i], - blk, - radix, - next_skip - 1, - tab - ); + blst_radix_print(&scan[i], blk, radix, next_skip - 1, tab); blk += radix; } tab -= 4; @@ -1018,11 +972,10 @@ main(int ac, char **av) for (;;) { char buf[1024]; - daddr_t da = 0; - daddr_t count = 0; - + long long da = 0; + long long count = 0; - printf("%lld/%lld/%lld> ", (long long)bl->bl_free, + printf("%lld/%lld/%lld> ", (long long)blist_avail(bl), (long long)size, (long long)bl->bl_radix); fflush(stdout); if (fgets(buf, sizeof(buf), stdin) == NULL) @@ -1030,7 +983,7 @@ main(int ac, char **av) switch(buf[0]) { case 'r': if (sscanf(buf + 1, "%lld", &count) == 1) { - blist_resize(&bl, count, 1); + blist_resize(&bl, count, 1, M_WAITOK); } else { printf("?\n"); } @@ -1046,18 +999,16 @@ main(int ac, char **av) } break; case 'f': - if (sscanf(buf + 1, "%llx %lld", - (long long *)&da, (long long *)&count) == 2) { + if (sscanf(buf + 1, "%llx %lld", &da, &count) == 2) { blist_free(bl, da, count); } else { printf("?\n"); } break; case 'l': - if (sscanf(buf + 1, "%llx %lld", - (long long *)&da, (long long *)&count) == 2) { - printf(" n=%d\n", - blist_fill(bl, da, count)); + if (sscanf(buf + 1, "%llx %lld", &da, &count) == 2) { + printf(" n=%jd\n", + (intmax_t)blist_fill(bl, da, count)); } else { printf("?\n"); } @@ -1094,4 +1045,3 @@ panic(const char *ctl, ...) } #endif - diff --git a/freebsd/sys/kern/subr_prf.c b/freebsd/sys/kern/subr_prf.c index 39f5826d..0380cfec 100644 --- a/freebsd/sys/kern/subr_prf.c +++ b/freebsd/sys/kern/subr_prf.c @@ -411,7 +411,6 @@ log_console(struct uio *uio) msgbuftrigger = 1; free(uio, M_IOV); free(consbuffer, M_TEMP); - return; } #endif /* __rtems__ */ @@ -678,7 +677,7 @@ kvprintf(char const *fmt, void (*func)(int, void*), void *arg, int radix, va_lis uintmax_t num; int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot; int cflag, hflag, jflag, tflag, zflag; - int dwidth, upper; + int bconv, dwidth, upper; char padc; int stop = 0, retval = 0; @@ -704,7 +703,7 @@ kvprintf(char const *fmt, void (*func)(int, void*), void *arg, int radix, va_lis } percent = fmt - 1; qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0; - sign = 0; dot = 0; dwidth = 0; upper = 0; + sign = 0; dot = 0; bconv = 0; dwidth = 0; upper = 0; cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0; reswitch: switch (ch = (u_char)*fmt++) { case '.': @@ -752,28 +751,9 @@ reswitch: switch (ch = (u_char)*fmt++) { width = n; goto reswitch; case 'b': - num = (u_int)va_arg(ap, int); - p = va_arg(ap, char *); - for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;) - PCHAR(*q--); - - if (num == 0) - break; - - for (tmp = 0; *p;) { - n = *p++; - if (num & (1 << (n - 1))) { - PCHAR(tmp ? ',' : '<'); - for (; (n = *p) > ' '; ++p) - PCHAR(n); - tmp = 1; - } else - for (; *p > ' '; ++p) - continue; - } - if (tmp) - PCHAR('>'); - break; + ladjust = 1; + bconv = 1; + goto handle_nosign; case 'c': width -= 1; @@ -919,6 +899,10 @@ handle_nosign: num = (u_char)va_arg(ap, int); else num = va_arg(ap, u_int); + if (bconv) { + q = va_arg(ap, char *); + base = *q++; + } goto number; handle_sign: if (jflag) @@ -976,6 +960,26 @@ number: while (*p) PCHAR(*p--); + if (bconv && num != 0) { + /* %b conversion flag format. */ + tmp = retval; + while (*q) { + n = *q++; + if (num & (1 << (n - 1))) { + PCHAR(retval != tmp ? + ',' : '<'); + for (; (n = *q) > ' '; ++q) + PCHAR(n); + } else + for (; *q > ' '; ++q) + continue; + } + if (retval != tmp) { + PCHAR('>'); + width -= retval - tmp; + } + } + if (ladjust) while (width-- > 0) PCHAR(' '); diff --git a/freebsd/sys/kern/subr_sbuf.c b/freebsd/sys/kern/subr_sbuf.c index 680613b1..8dd11b07 100644 --- a/freebsd/sys/kern/subr_sbuf.c +++ b/freebsd/sys/kern/subr_sbuf.c @@ -106,7 +106,7 @@ _assert_sbuf_integrity(const char *fun, struct sbuf *s) ("%s called with a NULL sbuf pointer", fun)); KASSERT(s->s_buf != NULL, ("%s called with uninitialized or corrupt sbuf", fun)); - if (SBUF_ISFINISHED(s) && SBUF_NULINCLUDED(s)) { + if (SBUF_ISFINISHED(s) && SBUF_NULINCLUDED(s)) { KASSERT(s->s_len <= s->s_size, ("wrote past end of sbuf (%jd >= %jd)", (intmax_t)s->s_len, (intmax_t)s->s_size)); diff --git a/freebsd/sys/kern/subr_taskqueue.c b/freebsd/sys/kern/subr_taskqueue.c index 6f1ba19a..74b9cf59 100644 --- a/freebsd/sys/kern/subr_taskqueue.c +++ b/freebsd/sys/kern/subr_taskqueue.c @@ -316,8 +316,8 @@ taskqueue_timeout_func(void *arg) } int -taskqueue_enqueue_timeout(struct taskqueue *queue, - struct timeout_task *timeout_task, int ticks) +taskqueue_enqueue_timeout_sbt(struct taskqueue *queue, + struct timeout_task *timeout_task, sbintime_t sbt, sbintime_t pr, int flags) { int res; @@ -333,7 +333,7 @@ taskqueue_enqueue_timeout(struct taskqueue *queue, /* Do nothing */ TQ_UNLOCK(queue); res = -1; - } else if (ticks == 0) { + } else if (sbt == 0) { taskqueue_enqueue_locked(queue, &timeout_task->t); /* The lock is released inside. */ } else { @@ -342,18 +342,27 @@ taskqueue_enqueue_timeout(struct taskqueue *queue, } else { queue->tq_callouts++; timeout_task->f |= DT_CALLOUT_ARMED; - if (ticks < 0) - ticks = -ticks; /* Ignore overflow. */ + if (sbt < 0) + sbt = -sbt; /* Ignore overflow. */ } - if (ticks > 0) { - callout_reset(&timeout_task->c, ticks, - taskqueue_timeout_func, timeout_task); + if (sbt > 0) { + callout_reset_sbt(&timeout_task->c, sbt, pr, + taskqueue_timeout_func, timeout_task, flags); } TQ_UNLOCK(queue); } return (res); } +int +taskqueue_enqueue_timeout(struct taskqueue *queue, + struct timeout_task *ttask, int ticks) +{ + + return (taskqueue_enqueue_timeout_sbt(queue, ttask, ticks * tick_sbt, + 0, 0)); +} + static void taskqueue_task_nop_fn(void *context, int pending) { diff --git a/freebsd/sys/kern/subr_uio.c b/freebsd/sys/kern/subr_uio.c index 5740e667..904ef1f4 100644 --- a/freebsd/sys/kern/subr_uio.c +++ b/freebsd/sys/kern/subr_uio.c @@ -212,41 +212,37 @@ uiomove_nofault(void *cp, int n, struct uio *uio) static int uiomove_faultflag(void *cp, int n, struct uio *uio, int nofault) { -#ifndef __rtems__ - struct thread *td; -#endif /* __rtems__ */ struct iovec *iov; size_t cnt; - int error, newflags, save; - #ifndef __rtems__ - td = curthread; + int error, newflags, save; +#else /* __rtems__ */ + int error; #endif /* __rtems__ */ + error = 0; +#ifndef __rtems__ KASSERT(uio->uio_rw == UIO_READ || uio->uio_rw == UIO_WRITE, ("uiomove: mode")); -#ifndef __rtems__ - KASSERT(uio->uio_segflg != UIO_USERSPACE || uio->uio_td == td, + KASSERT(uio->uio_segflg != UIO_USERSPACE || uio->uio_td == curthread, ("uiomove proc")); -#endif /* __rtems__ */ - if (!nofault) - WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, - "Calling uiomove()"); -#ifndef __rtems__ - /* XXX does it make a sense to set TDP_DEADLKTREAT for UIO_SYSSPACE ? */ - newflags = TDP_DEADLKTREAT; - if (uio->uio_segflg == UIO_USERSPACE && nofault) { - /* - * Fail if a non-spurious page fault occurs. - */ - newflags |= TDP_NOFAULTING | TDP_RESETSPUR; + if (uio->uio_segflg == UIO_USERSPACE) { + newflags = TDP_DEADLKTREAT; + if (nofault) { + /* + * Fail if a non-spurious page fault occurs. + */ + newflags |= TDP_NOFAULTING | TDP_RESETSPUR; + } else { + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, + "Calling uiomove()"); + } + save = curthread_pflags_set(newflags); + } else { + KASSERT(nofault == 0, ("uiomove: nofault")); } - save = curthread_pflags_set(newflags); -#else /* __rtems__ */ - (void) newflags; - (void) save; #endif /* __rtems__ */ while (n > 0 && uio->uio_resid) { @@ -292,7 +288,8 @@ uiomove_faultflag(void *cp, int n, struct uio *uio, int nofault) } out: #ifndef __rtems__ - curthread_pflags_restore(save); + if (uio->uio_segflg == UIO_USERSPACE) + curthread_pflags_restore(save); #endif /* __rtems__ */ return (error); } diff --git a/freebsd/sys/kern/sys_socket.c b/freebsd/sys/kern/sys_socket.c index 8d87c51b..9dd458f1 100644 --- a/freebsd/sys/kern/sys_socket.c +++ b/freebsd/sys/kern/sys_socket.c @@ -318,32 +318,36 @@ soo_ioctl(struct file *fp, u_long cmd, void *data, struct ucred *active_cred, break; case FIOASYNC: - /* - * XXXRW: This code separately acquires SOCK_LOCK(so) and - * SOCKBUF_LOCK(&so->so_rcv) even though they are the same - * mutex to avoid introducing the assumption that they are - * the same. - */ if (*(int *)data) { SOCK_LOCK(so); so->so_state |= SS_ASYNC; + if (SOLISTENING(so)) { + so->sol_sbrcv_flags |= SB_ASYNC; + so->sol_sbsnd_flags |= SB_ASYNC; + } else { + SOCKBUF_LOCK(&so->so_rcv); + so->so_rcv.sb_flags |= SB_ASYNC; + SOCKBUF_UNLOCK(&so->so_rcv); + SOCKBUF_LOCK(&so->so_snd); + so->so_snd.sb_flags |= SB_ASYNC; + SOCKBUF_UNLOCK(&so->so_snd); + } SOCK_UNLOCK(so); - SOCKBUF_LOCK(&so->so_rcv); - so->so_rcv.sb_flags |= SB_ASYNC; - SOCKBUF_UNLOCK(&so->so_rcv); - SOCKBUF_LOCK(&so->so_snd); - so->so_snd.sb_flags |= SB_ASYNC; - SOCKBUF_UNLOCK(&so->so_snd); } else { SOCK_LOCK(so); so->so_state &= ~SS_ASYNC; + if (SOLISTENING(so)) { + so->sol_sbrcv_flags &= ~SB_ASYNC; + so->sol_sbsnd_flags &= ~SB_ASYNC; + } else { + SOCKBUF_LOCK(&so->so_rcv); + so->so_rcv.sb_flags &= ~SB_ASYNC; + SOCKBUF_UNLOCK(&so->so_rcv); + SOCKBUF_LOCK(&so->so_snd); + so->so_snd.sb_flags &= ~SB_ASYNC; + SOCKBUF_UNLOCK(&so->so_snd); + } SOCK_UNLOCK(so); - SOCKBUF_LOCK(&so->so_rcv); - so->so_rcv.sb_flags &= ~SB_ASYNC; - SOCKBUF_UNLOCK(&so->so_rcv); - SOCKBUF_LOCK(&so->so_snd); - so->so_snd.sb_flags &= ~SB_ASYNC; - SOCKBUF_UNLOCK(&so->so_snd); } break; @@ -477,7 +481,6 @@ static int soo_stat(struct socket *so, struct stat *ub) { #endif /* __rtems__ */ - struct sockbuf *sb; #ifdef MAC int error; #endif @@ -491,22 +494,26 @@ soo_stat(struct socket *so, struct stat *ub) if (error) return (error); #endif - /* - * If SBS_CANTRCVMORE is set, but there's still data left in the - * receive buffer, the socket is still readable. - */ - sb = &so->so_rcv; - SOCKBUF_LOCK(sb); - if ((sb->sb_state & SBS_CANTRCVMORE) == 0 || sbavail(sb)) - ub->st_mode |= S_IRUSR | S_IRGRP | S_IROTH; - ub->st_size = sbavail(sb) - sb->sb_ctl; - SOCKBUF_UNLOCK(sb); + if (!SOLISTENING(so)) { + struct sockbuf *sb; - sb = &so->so_snd; - SOCKBUF_LOCK(sb); - if ((sb->sb_state & SBS_CANTSENDMORE) == 0) - ub->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; - SOCKBUF_UNLOCK(sb); + /* + * If SBS_CANTRCVMORE is set, but there's still data left + * in the receive buffer, the socket is still readable. + */ + sb = &so->so_rcv; + SOCKBUF_LOCK(sb); + if ((sb->sb_state & SBS_CANTRCVMORE) == 0 || sbavail(sb)) + ub->st_mode |= S_IRUSR | S_IRGRP | S_IROTH; + ub->st_size = sbavail(sb) - sb->sb_ctl; + SOCKBUF_UNLOCK(sb); + + sb = &so->so_snd; + SOCKBUF_LOCK(sb); + if ((sb->sb_state & SBS_CANTSENDMORE) == 0) + ub->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; + SOCKBUF_UNLOCK(sb); + } #ifndef __rtems__ ub->st_uid = so->so_cred->cr_uid; ub->st_gid = so->so_cred->cr_gid; @@ -916,6 +923,7 @@ soaio_process_sb(struct socket *so, struct sockbuf *sb) { struct kaiocb *job; + CURVNET_SET(so->so_vnet); SOCKBUF_LOCK(sb); while (!TAILQ_EMPTY(&sb->sb_aiojobq) && soaio_ready(so, sb)) { job = TAILQ_FIRST(&sb->sb_aiojobq); @@ -936,9 +944,9 @@ soaio_process_sb(struct socket *so, struct sockbuf *sb) sb->sb_flags &= ~SB_AIO_RUNNING; SOCKBUF_UNLOCK(sb); - ACCEPT_LOCK(); SOCK_LOCK(so); sorele(so); + CURVNET_RESTORE(); } void diff --git a/freebsd/sys/kern/uipc_accf.c b/freebsd/sys/kern/uipc_accf.c index a766adf8..8a0e14e3 100644 --- a/freebsd/sys/kern/uipc_accf.c +++ b/freebsd/sys/kern/uipc_accf.c @@ -132,8 +132,7 @@ accept_filt_generic_mod_event(module_t mod, int event, void *data) switch (event) { case MOD_LOAD: - p = malloc(sizeof(*p), M_ACCF, - M_WAITOK); + p = malloc(sizeof(*p), M_ACCF, M_WAITOK); bcopy(accfp, p, sizeof(*p)); error = accept_filt_add(p); break; @@ -164,26 +163,25 @@ accept_filt_generic_mod_event(module_t mod, int event, void *data) } int -do_getopt_accept_filter(struct socket *so, struct sockopt *sopt) +accept_filt_getopt(struct socket *so, struct sockopt *sopt) { struct accept_filter_arg *afap; int error; error = 0; - afap = malloc(sizeof(*afap), M_TEMP, - M_WAITOK | M_ZERO); + afap = malloc(sizeof(*afap), M_TEMP, M_WAITOK | M_ZERO); SOCK_LOCK(so); if ((so->so_options & SO_ACCEPTCONN) == 0) { error = EINVAL; goto out; } - if ((so->so_options & SO_ACCEPTFILTER) == 0) { + if (so->sol_accept_filter == NULL) { error = EINVAL; goto out; } - strcpy(afap->af_name, so->so_accf->so_accept_filter->accf_name); - if (so->so_accf->so_accept_filter_str != NULL) - strcpy(afap->af_arg, so->so_accf->so_accept_filter_str); + strcpy(afap->af_name, so->sol_accept_filter->accf_name); + if (so->sol_accept_filter_str != NULL) + strcpy(afap->af_arg, so->sol_accept_filter_str); out: SOCK_UNLOCK(so); if (error == 0) @@ -193,35 +191,61 @@ out: } int -do_setopt_accept_filter(struct socket *so, struct sockopt *sopt) +accept_filt_setopt(struct socket *so, struct sockopt *sopt) { struct accept_filter_arg *afap; struct accept_filter *afp; - struct so_accf *newaf; - int error = 0; + char *accept_filter_str = NULL; + void *accept_filter_arg = NULL; + int error; /* * Handle the simple delete case first. */ if (sopt == NULL || sopt->sopt_val == NULL) { + struct socket *sp, *sp1; + int wakeup; + SOCK_LOCK(so); if ((so->so_options & SO_ACCEPTCONN) == 0) { SOCK_UNLOCK(so); return (EINVAL); } - if (so->so_accf != NULL) { - struct so_accf *af = so->so_accf; - if (af->so_accept_filter != NULL && - af->so_accept_filter->accf_destroy != NULL) { - af->so_accept_filter->accf_destroy(so); - } - if (af->so_accept_filter_str != NULL) - free(af->so_accept_filter_str, M_ACCF); - free(af, M_ACCF); - so->so_accf = NULL; + if (so->sol_accept_filter == NULL) { + SOCK_UNLOCK(so); + return (0); } + if (so->sol_accept_filter->accf_destroy != NULL) + so->sol_accept_filter->accf_destroy(so); + if (so->sol_accept_filter_str != NULL) + free(so->sol_accept_filter_str, M_ACCF); + so->sol_accept_filter = NULL; + so->sol_accept_filter_arg = NULL; + so->sol_accept_filter_str = NULL; so->so_options &= ~SO_ACCEPTFILTER; - SOCK_UNLOCK(so); + + /* + * Move from incomplete queue to complete only those + * connections, that are blocked by us. + */ + wakeup = 0; + TAILQ_FOREACH_SAFE(sp, &so->sol_incomp, so_list, sp1) { + SOCK_LOCK(sp); + if (sp->so_options & SO_ACCEPTFILTER) { + TAILQ_REMOVE(&so->sol_incomp, sp, so_list); + TAILQ_INSERT_TAIL(&so->sol_comp, sp, so_list); + sp->so_qstate = SQ_COMP; + sp->so_options &= ~SO_ACCEPTFILTER; + so->sol_incqlen--; + so->sol_qlen++; + wakeup = 1; + } + SOCK_UNLOCK(sp); + } + if (wakeup) + solisten_wakeup(so); /* unlocks */ + else + SOLISTEN_UNLOCK(so); return (0); } @@ -229,8 +253,7 @@ do_setopt_accept_filter(struct socket *so, struct sockopt *sopt) * Pre-allocate any memory we may need later to avoid blocking at * untimely moments. This does not optimize for invalid arguments. */ - afap = malloc(sizeof(*afap), M_TEMP, - M_WAITOK); + afap = malloc(sizeof(*afap), M_TEMP, M_WAITOK); error = sooptcopyin(sopt, afap, sizeof *afap, sizeof *afap); afap->af_name[sizeof(afap->af_name)-1] = '\0'; afap->af_arg[sizeof(afap->af_arg)-1] = '\0'; @@ -243,19 +266,10 @@ do_setopt_accept_filter(struct socket *so, struct sockopt *sopt) free(afap, M_TEMP); return (ENOENT); } - /* - * Allocate the new accept filter instance storage. We may - * have to free it again later if we fail to attach it. If - * attached properly, 'newaf' is NULLed to avoid a free() - * while in use. - */ - newaf = malloc(sizeof(*newaf), M_ACCF, M_WAITOK | - M_ZERO); if (afp->accf_create != NULL && afap->af_name[0] != '\0') { size_t len = strlen(afap->af_name) + 1; - newaf->so_accept_filter_str = malloc(len, M_ACCF, - M_WAITOK); - strcpy(newaf->so_accept_filter_str, afap->af_name); + accept_filter_str = malloc(len, M_ACCF, M_WAITOK); + strcpy(accept_filter_str, afap->af_name); } /* @@ -263,8 +277,8 @@ do_setopt_accept_filter(struct socket *so, struct sockopt *sopt) * without first removing it. */ SOCK_LOCK(so); - if (((so->so_options & SO_ACCEPTCONN) == 0) || - (so->so_accf != NULL)) { + if ((so->so_options & SO_ACCEPTCONN) == 0 || + so->sol_accept_filter != NULL) { error = EINVAL; goto out; } @@ -275,25 +289,20 @@ do_setopt_accept_filter(struct socket *so, struct sockopt *sopt) * can't block. */ if (afp->accf_create != NULL) { - newaf->so_accept_filter_arg = - afp->accf_create(so, afap->af_arg); - if (newaf->so_accept_filter_arg == NULL) { + accept_filter_arg = afp->accf_create(so, afap->af_arg); + if (accept_filter_arg == NULL) { error = EINVAL; goto out; } } - newaf->so_accept_filter = afp; - so->so_accf = newaf; + so->sol_accept_filter = afp; + so->sol_accept_filter_arg = accept_filter_arg; + so->sol_accept_filter_str = accept_filter_str; so->so_options |= SO_ACCEPTFILTER; - newaf = NULL; out: SOCK_UNLOCK(so); - if (newaf != NULL) { - if (newaf->so_accept_filter_str != NULL) - free(newaf->so_accept_filter_str, M_ACCF); - free(newaf, M_ACCF); - } - if (afap != NULL) - free(afap, M_TEMP); + if (accept_filter_str != NULL) + free(accept_filter_str, M_ACCF); + free(afap, M_TEMP); return (error); } diff --git a/freebsd/sys/kern/uipc_mbuf.c b/freebsd/sys/kern/uipc_mbuf.c index ba8a2d48..abc30dd3 100644 --- a/freebsd/sys/kern/uipc_mbuf.c +++ b/freebsd/sys/kern/uipc_mbuf.c @@ -1519,7 +1519,7 @@ m_uiotombuf(struct uio *uio, int how, int len, int align, int flags) * the total data supplied by the uio. */ if (len > 0) - total = min(uio->uio_resid, len); + total = (uio->uio_resid < len) ? uio->uio_resid : len; else total = uio->uio_resid; diff --git a/freebsd/sys/kern/uipc_sockbuf.c b/freebsd/sys/kern/uipc_sockbuf.c index 04193c29..4b710a2c 100644 --- a/freebsd/sys/kern/uipc_sockbuf.c +++ b/freebsd/sys/kern/uipc_sockbuf.c @@ -316,14 +316,14 @@ sowakeup(struct socket *so, struct sockbuf *sb) SOCKBUF_LOCK_ASSERT(sb); - selwakeuppri(&sb->sb_sel, PSOCK); - if (!SEL_WAITING(&sb->sb_sel)) + selwakeuppri(sb->sb_sel, PSOCK); + if (!SEL_WAITING(sb->sb_sel)) sb->sb_flags &= ~SB_SEL; if (sb->sb_flags & SB_WAIT) { sb->sb_flags &= ~SB_WAIT; wakeup(&sb->sb_acc); } - KNOTE_LOCKED(&sb->sb_sel.si_note, 0); + KNOTE_LOCKED(&sb->sb_sel->si_note, 0); if (sb->sb_upcall != NULL) { ret = sb->sb_upcall(so, sb->sb_upcallarg, M_NOWAIT); if (ret == SU_ISCONNECTED) { @@ -336,7 +336,7 @@ sowakeup(struct socket *so, struct sockbuf *sb) if (sb->sb_flags & SB_AIO) sowakeup_aio(so, sb); SOCKBUF_UNLOCK(sb); - if (ret == SU_ISCONNECTED) + if (ret == SU_ISCONNECTED && !(so->so_state & SS_ISDISCONNECTED)) soisconnected(so); if ((so->so_state & SS_ASYNC) && so->so_sigio != NULL) pgsigio(&so->so_sigio, SIGIO, 0); @@ -457,14 +457,78 @@ sbreserve_locked(struct sockbuf *sb, u_long cc, struct socket *so, } int -sbreserve(struct sockbuf *sb, u_long cc, struct socket *so, - struct thread *td) +sbsetopt(struct socket *so, int cmd, u_long cc) { + struct sockbuf *sb; + short *flags; + u_int *hiwat, *lowat; int error; - SOCKBUF_LOCK(sb); - error = sbreserve_locked(sb, cc, so, td); - SOCKBUF_UNLOCK(sb); + SOCK_LOCK(so); + if (SOLISTENING(so)) { + switch (cmd) { + case SO_SNDLOWAT: + case SO_SNDBUF: + lowat = &so->sol_sbsnd_lowat; + hiwat = &so->sol_sbsnd_hiwat; + flags = &so->sol_sbsnd_flags; + break; + case SO_RCVLOWAT: + case SO_RCVBUF: + lowat = &so->sol_sbrcv_lowat; + hiwat = &so->sol_sbrcv_hiwat; + flags = &so->sol_sbrcv_flags; + break; + } + } else { + switch (cmd) { + case SO_SNDLOWAT: + case SO_SNDBUF: + sb = &so->so_snd; + break; + case SO_RCVLOWAT: + case SO_RCVBUF: + sb = &so->so_rcv; + break; + } + flags = &sb->sb_flags; + hiwat = &sb->sb_hiwat; + lowat = &sb->sb_lowat; + SOCKBUF_LOCK(sb); + } + + error = 0; + switch (cmd) { + case SO_SNDBUF: + case SO_RCVBUF: + if (SOLISTENING(so)) { + if (cc > sb_max_adj) { + error = ENOBUFS; + break; + } + *hiwat = cc; + if (*lowat > *hiwat) + *lowat = *hiwat; + } else { + if (!sbreserve_locked(sb, cc, so, curthread)) + error = ENOBUFS; + } + if (error == 0) + *flags &= ~SB_AUTOSIZE; + break; + case SO_SNDLOWAT: + case SO_RCVLOWAT: + /* + * Make sure the low-water is never greater than the + * high-water. + */ + *lowat = (cc > *hiwat) ? *hiwat : cc; + break; + } + + if (!SOLISTENING(so)) + SOCKBUF_UNLOCK(sb); + SOCK_UNLOCK(so); return (error); } diff --git a/freebsd/sys/kern/uipc_socket.c b/freebsd/sys/kern/uipc_socket.c index c52a543c..1773606d 100644 --- a/freebsd/sys/kern/uipc_socket.c +++ b/freebsd/sys/kern/uipc_socket.c @@ -108,6 +108,7 @@ __FBSDID("$FreeBSD$"); #include <rtems/bsd/local/opt_inet.h> #include <rtems/bsd/local/opt_inet6.h> #include <rtems/bsd/local/opt_compat.h> +#include <rtems/bsd/local/opt_sctp.h> #include <sys/param.h> #include <sys/systm.h> @@ -160,13 +161,21 @@ __FBSDID("$FreeBSD$"); static int soreceive_rcvoob(struct socket *so, struct uio *uio, int flags); +static void so_rdknl_lock(void *); +static void so_rdknl_unlock(void *); +static void so_rdknl_assert_locked(void *); +static void so_rdknl_assert_unlocked(void *); +static void so_wrknl_lock(void *); +static void so_wrknl_unlock(void *); +static void so_wrknl_assert_locked(void *); +static void so_wrknl_assert_unlocked(void *); static void filt_sordetach(struct knote *kn); static int filt_soread(struct knote *kn, long hint); static void filt_sowdetach(struct knote *kn); static int filt_sowrite(struct knote *kn, long hint); -static int inline hhook_run_socket(struct socket *so, void *hctx, int32_t h_id); static int filt_soempty(struct knote *kn, long hint); +static int inline hhook_run_socket(struct socket *so, void *hctx, int32_t h_id); #ifdef __rtems__ static #endif /* __rtems__ */ @@ -412,8 +421,16 @@ soalloc(struct vnet *vnet) return (NULL); } + /* + * The socket locking protocol allows to lock 2 sockets at a time, + * however, the first one must be a listening socket. WITNESS lacks + * a feature to change class of an existing lock, so we use DUPOK. + */ + mtx_init(&so->so_lock, "socket", NULL, MTX_DEF | MTX_DUPOK); SOCKBUF_LOCK_INIT(&so->so_snd, "so_snd"); SOCKBUF_LOCK_INIT(&so->so_rcv, "so_rcv"); + so->so_rcv.sb_sel = &so->so_rdsel; + so->so_snd.sb_sel = &so->so_wrsel; sx_init(&so->so_snd.sb_sx, "so_snd_sx"); sx_init(&so->so_rcv.sb_sx, "so_rcv_sx"); #ifndef __rtems__ @@ -465,15 +482,6 @@ sodealloc(struct socket *so) so->so_vnet->vnet_sockcnt--; #endif mtx_unlock(&so_global_mtx); - if (so->so_rcv.sb_hiwat) - (void)chgsbsize(so->so_cred->cr_uidinfo, - &so->so_rcv.sb_hiwat, 0, RLIM_INFINITY); - if (so->so_snd.sb_hiwat) - (void)chgsbsize(so->so_cred->cr_uidinfo, - &so->so_snd.sb_hiwat, 0, RLIM_INFINITY); - /* remove accept filter if one is present. */ - if (so->so_accf != NULL) - do_setopt_accept_filter(so, NULL); #ifdef MAC mac_socket_destroy(so); #endif @@ -481,10 +489,22 @@ sodealloc(struct socket *so) crfree(so->so_cred); khelp_destroy_osd(&so->osd); - sx_destroy(&so->so_snd.sb_sx); - sx_destroy(&so->so_rcv.sb_sx); - SOCKBUF_LOCK_DESTROY(&so->so_snd); - SOCKBUF_LOCK_DESTROY(&so->so_rcv); + if (SOLISTENING(so)) { + if (so->sol_accept_filter != NULL) + accept_filt_setopt(so, NULL); + } else { + if (so->so_rcv.sb_hiwat) + (void)chgsbsize(so->so_cred->cr_uidinfo, + &so->so_rcv.sb_hiwat, 0, RLIM_INFINITY); + if (so->so_snd.sb_hiwat) + (void)chgsbsize(so->so_cred->cr_uidinfo, + &so->so_snd.sb_hiwat, 0, RLIM_INFINITY); + sx_destroy(&so->so_snd.sb_sx); + sx_destroy(&so->so_rcv.sb_sx); + SOCKBUF_LOCK_DESTROY(&so->so_snd); + SOCKBUF_LOCK_DESTROY(&so->so_rcv); + } + mtx_destroy(&so->so_lock); uma_zfree(socket_zone, so); } @@ -527,8 +547,6 @@ socreate(int dom, struct socket **aso, int type, int proto, if (so == NULL) return (ENOBUFS); - TAILQ_INIT(&so->so_incomp); - TAILQ_INIT(&so->so_comp); so->so_type = type; so->so_cred = crhold(cred); if ((prp->pr_domain->dom_family == PF_INET) || @@ -545,9 +563,10 @@ socreate(int dom, struct socket **aso, int type, int proto, #ifdef MAC mac_socket_create(cred, so); #endif - knlist_init_mtx(&so->so_rcv.sb_sel.si_note, SOCKBUF_MTX(&so->so_rcv)); - knlist_init_mtx(&so->so_snd.sb_sel.si_note, SOCKBUF_MTX(&so->so_snd)); - so->so_count = 1; + knlist_init(&so->so_rdsel.si_note, so, so_rdknl_lock, so_rdknl_unlock, + so_rdknl_assert_locked, so_rdknl_assert_unlocked); + knlist_init(&so->so_wrsel.si_note, so, so_wrknl_lock, so_wrknl_unlock, + so_wrknl_assert_locked, so_wrknl_assert_unlocked); /* * Auto-sizing of socket buffers is managed by the protocols and * the appropriate flags must be set in the pru_attach function. @@ -556,12 +575,10 @@ socreate(int dom, struct socket **aso, int type, int proto, error = (*prp->pr_usrreqs->pru_attach)(so, proto, td); CURVNET_RESTORE(); if (error) { - KASSERT(so->so_count == 1, ("socreate: so_count %d", - so->so_count)); - so->so_count = 0; sodealloc(so); return (error); } + soref(so); *aso = so; return (0); } @@ -589,11 +606,11 @@ sonewconn(struct socket *head, int connstatus) static int overcount; struct socket *so; - int over; + u_int over; - ACCEPT_LOCK(); - over = (head->so_qlen > 3 * head->so_qlimit / 2); - ACCEPT_UNLOCK(); + SOLISTEN_LOCK(head); + over = (head->sol_qlen > 3 * head->sol_qlimit / 2); + SOLISTEN_UNLOCK(head); #ifdef REGRESSION if (regression_sonewconn_earlytest && over) { #else @@ -605,15 +622,15 @@ sonewconn(struct socket *head, int connstatus) log(LOG_DEBUG, "%s: pcb %p: Listen queue overflow: " "%i already in queue awaiting acceptance " "(%d occurrences)\n", - __func__, head->so_pcb, head->so_qlen, overcount); + __func__, head->so_pcb, head->sol_qlen, overcount); overcount = 0; } return (NULL); } - VNET_ASSERT(head->so_vnet != NULL, ("%s:%d so_vnet is NULL, head=%p", - __func__, __LINE__, head)); + VNET_ASSERT(head->so_vnet != NULL, ("%s: so %p vnet is NULL", + __func__, head)); so = soalloc(head->so_vnet); if (so == NULL) { log(LOG_DEBUG, "%s: pcb %p: New socket allocation failure: " @@ -621,11 +638,8 @@ sonewconn(struct socket *head, int connstatus) __func__, head->so_pcb); return (NULL); } - if ((head->so_options & SO_ACCEPTFILTER) != 0) - connstatus = 0; - so->so_head = head; + so->so_listen = head; so->so_type = head->so_type; - so->so_options = head->so_options &~ SO_ACCEPTCONN; so->so_linger = head->so_linger; so->so_state = head->so_state | SS_NOFDREF; so->so_fibnum = head->so_fibnum; @@ -634,10 +648,12 @@ sonewconn(struct socket *head, int connstatus) #ifdef MAC mac_socket_newconn(head, so); #endif - knlist_init_mtx(&so->so_rcv.sb_sel.si_note, SOCKBUF_MTX(&so->so_rcv)); - knlist_init_mtx(&so->so_snd.sb_sel.si_note, SOCKBUF_MTX(&so->so_snd)); + knlist_init(&so->so_rdsel.si_note, so, so_rdknl_lock, so_rdknl_unlock, + so_rdknl_assert_locked, so_rdknl_assert_unlocked); + knlist_init(&so->so_wrsel.si_note, so, so_wrknl_lock, so_wrknl_unlock, + so_wrknl_assert_locked, so_wrknl_assert_unlocked); VNET_SO_ASSERT(head); - if (soreserve(so, head->so_snd.sb_hiwat, head->so_rcv.sb_hiwat)) { + if (soreserve(so, head->sol_sbsnd_hiwat, head->sol_sbrcv_hiwat)) { sodealloc(so); log(LOG_DEBUG, "%s: pcb %p: soreserve() failed\n", __func__, head->so_pcb); @@ -649,32 +665,24 @@ sonewconn(struct socket *head, int connstatus) __func__, head->so_pcb); return (NULL); } - so->so_rcv.sb_lowat = head->so_rcv.sb_lowat; - so->so_snd.sb_lowat = head->so_snd.sb_lowat; - so->so_rcv.sb_timeo = head->so_rcv.sb_timeo; - so->so_snd.sb_timeo = head->so_snd.sb_timeo; - so->so_rcv.sb_flags |= head->so_rcv.sb_flags & SB_AUTOSIZE; - so->so_snd.sb_flags |= head->so_snd.sb_flags & SB_AUTOSIZE; + so->so_rcv.sb_lowat = head->sol_sbrcv_lowat; + so->so_snd.sb_lowat = head->sol_sbsnd_lowat; + so->so_rcv.sb_timeo = head->sol_sbrcv_timeo; + so->so_snd.sb_timeo = head->sol_sbsnd_timeo; + so->so_rcv.sb_flags |= head->sol_sbrcv_flags & SB_AUTOSIZE; + so->so_snd.sb_flags |= head->sol_sbsnd_flags & SB_AUTOSIZE; + + SOLISTEN_LOCK(head); + if (head->sol_accept_filter != NULL) + connstatus = 0; so->so_state |= connstatus; - ACCEPT_LOCK(); - /* - * The accept socket may be tearing down but we just - * won a race on the ACCEPT_LOCK. - * However, if sctp_peeloff() is called on a 1-to-many - * style socket, the SO_ACCEPTCONN doesn't need to be set. - */ - if (!(head->so_options & SO_ACCEPTCONN) && - ((head->so_proto->pr_protocol != IPPROTO_SCTP) || - (head->so_type != SOCK_SEQPACKET))) { - SOCK_LOCK(so); - so->so_head = NULL; - sofree(so); /* NB: returns ACCEPT_UNLOCK'ed. */ - return (NULL); - } + so->so_options = head->so_options & ~SO_ACCEPTCONN; + soref(head); /* A socket on (in)complete queue refs head. */ if (connstatus) { - TAILQ_INSERT_TAIL(&head->so_comp, so, so_list); - so->so_qstate |= SQ_COMP; - head->so_qlen++; + TAILQ_INSERT_TAIL(&head->sol_comp, so, so_list); + so->so_qstate = SQ_COMP; + head->sol_qlen++; + solisten_wakeup(head); /* unlocks */ } else { /* * Keep removing sockets from the head until there's room for @@ -683,28 +691,86 @@ sonewconn(struct socket *head, int connstatus) * threads and soabort() requires dropping locks, we must * loop waiting for the condition to be true. */ - while (head->so_incqlen > head->so_qlimit) { + while (head->sol_incqlen > head->sol_qlimit) { struct socket *sp; - sp = TAILQ_FIRST(&head->so_incomp); - TAILQ_REMOVE(&head->so_incomp, sp, so_list); - head->so_incqlen--; - sp->so_qstate &= ~SQ_INCOMP; - sp->so_head = NULL; - ACCEPT_UNLOCK(); + + sp = TAILQ_FIRST(&head->sol_incomp); + TAILQ_REMOVE(&head->sol_incomp, sp, so_list); + head->sol_incqlen--; + SOCK_LOCK(sp); + sp->so_qstate = SQ_NONE; + sp->so_listen = NULL; + SOCK_UNLOCK(sp); + sorele(head); /* does SOLISTEN_UNLOCK, head stays */ soabort(sp); - ACCEPT_LOCK(); + SOLISTEN_LOCK(head); } - TAILQ_INSERT_TAIL(&head->so_incomp, so, so_list); - so->so_qstate |= SQ_INCOMP; - head->so_incqlen++; + TAILQ_INSERT_TAIL(&head->sol_incomp, so, so_list); + so->so_qstate = SQ_INCOMP; + head->sol_incqlen++; + SOLISTEN_UNLOCK(head); } - ACCEPT_UNLOCK(); - if (connstatus) { - sorwakeup(head); - wakeup_one(&head->so_timeo); + return (so); +} + +#ifdef SCTP +/* + * Socket part of sctp_peeloff(). Detach a new socket from an + * association. The new socket is returned with a reference. + */ +struct socket * +sopeeloff(struct socket *head) +{ + struct socket *so; + + VNET_ASSERT(head->so_vnet != NULL, ("%s:%d so_vnet is NULL, head=%p", + __func__, __LINE__, head)); + so = soalloc(head->so_vnet); + if (so == NULL) { + log(LOG_DEBUG, "%s: pcb %p: New socket allocation failure: " + "limit reached or out of memory\n", + __func__, head->so_pcb); + return (NULL); } + so->so_type = head->so_type; + so->so_options = head->so_options; + so->so_linger = head->so_linger; + so->so_state = (head->so_state & SS_NBIO) | SS_ISCONNECTED; + so->so_fibnum = head->so_fibnum; + so->so_proto = head->so_proto; + so->so_cred = crhold(head->so_cred); +#ifdef MAC + mac_socket_newconn(head, so); +#endif + knlist_init(&so->so_rdsel.si_note, so, so_rdknl_lock, so_rdknl_unlock, + so_rdknl_assert_locked, so_rdknl_assert_unlocked); + knlist_init(&so->so_wrsel.si_note, so, so_wrknl_lock, so_wrknl_unlock, + so_wrknl_assert_locked, so_wrknl_assert_unlocked); + VNET_SO_ASSERT(head); + if (soreserve(so, head->so_snd.sb_hiwat, head->so_rcv.sb_hiwat)) { + sodealloc(so); + log(LOG_DEBUG, "%s: pcb %p: soreserve() failed\n", + __func__, head->so_pcb); + return (NULL); + } + if ((*so->so_proto->pr_usrreqs->pru_attach)(so, 0, NULL)) { + sodealloc(so); + log(LOG_DEBUG, "%s: pcb %p: pru_attach() failed\n", + __func__, head->so_pcb); + return (NULL); + } + so->so_rcv.sb_lowat = head->so_rcv.sb_lowat; + so->so_snd.sb_lowat = head->so_snd.sb_lowat; + so->so_rcv.sb_timeo = head->so_rcv.sb_timeo; + so->so_snd.sb_timeo = head->so_snd.sb_timeo; + so->so_rcv.sb_flags |= head->so_rcv.sb_flags & SB_AUTOSIZE; + so->so_snd.sb_flags |= head->so_snd.sb_flags & SB_AUTOSIZE; + + soref(so); + return (so); } +#endif /* SCTP */ int sobind(struct socket *so, struct sockaddr *nam, struct thread *td) @@ -766,13 +832,140 @@ solisten_proto_check(struct socket *so) void solisten_proto(struct socket *so, int backlog) { + int sbrcv_lowat, sbsnd_lowat; + u_int sbrcv_hiwat, sbsnd_hiwat; + short sbrcv_flags, sbsnd_flags; + sbintime_t sbrcv_timeo, sbsnd_timeo; SOCK_LOCK_ASSERT(so); + if (SOLISTENING(so)) + goto listening; + + /* + * Change this socket to listening state. + */ + sbrcv_lowat = so->so_rcv.sb_lowat; + sbsnd_lowat = so->so_snd.sb_lowat; + sbrcv_hiwat = so->so_rcv.sb_hiwat; + sbsnd_hiwat = so->so_snd.sb_hiwat; + sbrcv_flags = so->so_rcv.sb_flags; + sbsnd_flags = so->so_snd.sb_flags; + sbrcv_timeo = so->so_rcv.sb_timeo; + sbsnd_timeo = so->so_snd.sb_timeo; + + sbdestroy(&so->so_snd, so); + sbdestroy(&so->so_rcv, so); + sx_destroy(&so->so_snd.sb_sx); + sx_destroy(&so->so_rcv.sb_sx); + SOCKBUF_LOCK_DESTROY(&so->so_snd); + SOCKBUF_LOCK_DESTROY(&so->so_rcv); + +#ifdef INVARIANTS + bzero(&so->so_rcv, + sizeof(struct socket) - offsetof(struct socket, so_rcv)); +#endif + + so->sol_sbrcv_lowat = sbrcv_lowat; + so->sol_sbsnd_lowat = sbsnd_lowat; + so->sol_sbrcv_hiwat = sbrcv_hiwat; + so->sol_sbsnd_hiwat = sbsnd_hiwat; + so->sol_sbrcv_flags = sbrcv_flags; + so->sol_sbsnd_flags = sbsnd_flags; + so->sol_sbrcv_timeo = sbrcv_timeo; + so->sol_sbsnd_timeo = sbsnd_timeo; + + so->sol_qlen = so->sol_incqlen = 0; + TAILQ_INIT(&so->sol_incomp); + TAILQ_INIT(&so->sol_comp); + + so->sol_accept_filter = NULL; + so->sol_accept_filter_arg = NULL; + so->sol_accept_filter_str = NULL; + + so->sol_upcall = NULL; + so->sol_upcallarg = NULL; + + so->so_options |= SO_ACCEPTCONN; + +listening: if (backlog < 0 || backlog > somaxconn) backlog = somaxconn; - so->so_qlimit = backlog; - so->so_options |= SO_ACCEPTCONN; + so->sol_qlimit = backlog; +} + +/* + * Wakeup listeners/subsystems once we have a complete connection. + * Enters with lock, returns unlocked. + */ +void +solisten_wakeup(struct socket *sol) +{ + + if (sol->sol_upcall != NULL) + (void )sol->sol_upcall(sol, sol->sol_upcallarg, M_NOWAIT); + else { + selwakeuppri(&sol->so_rdsel, PSOCK); + KNOTE_LOCKED(&sol->so_rdsel.si_note, 0); + } + SOLISTEN_UNLOCK(sol); + wakeup_one(&sol->sol_comp); +} + +/* + * Return single connection off a listening socket queue. Main consumer of + * the function is kern_accept4(). Some modules, that do their own accept + * management also use the function. + * + * Listening socket must be locked on entry and is returned unlocked on + * return. + * The flags argument is set of accept4(2) flags and ACCEPT4_INHERIT. + */ +int +solisten_dequeue(struct socket *head, struct socket **ret, int flags) +{ + struct socket *so; + int error; + + SOLISTEN_LOCK_ASSERT(head); + + while (!(head->so_state & SS_NBIO) && TAILQ_EMPTY(&head->sol_comp) && + head->so_error == 0) { + error = msleep(&head->sol_comp, &head->so_lock, PSOCK | PCATCH, + "accept", 0); + if (error != 0) { + SOLISTEN_UNLOCK(head); + return (error); + } + } + if (head->so_error) { + error = head->so_error; + head->so_error = 0; + SOLISTEN_UNLOCK(head); + return (error); + } + if ((head->so_state & SS_NBIO) && TAILQ_EMPTY(&head->sol_comp)) { + SOLISTEN_UNLOCK(head); + return (EWOULDBLOCK); + } + so = TAILQ_FIRST(&head->sol_comp); + SOCK_LOCK(so); + KASSERT(so->so_qstate == SQ_COMP, + ("%s: so %p not SQ_COMP", __func__, so)); + soref(so); + head->sol_qlen--; + so->so_qstate = SQ_NONE; + so->so_listen = NULL; + TAILQ_REMOVE(&head->sol_comp, so, so_list); + if (flags & ACCEPT4_INHERIT) + so->so_state |= (head->so_state & SS_NBIO); + else + so->so_state |= (flags & SOCK_NONBLOCK) ? SS_NBIO : 0; + SOCK_UNLOCK(so); + sorele(head); + + *ret = so; + return (0); } /* @@ -799,44 +992,62 @@ void sofree(struct socket *so) { struct protosw *pr = so->so_proto; - struct socket *head; - ACCEPT_LOCK_ASSERT(); SOCK_LOCK_ASSERT(so); if ((so->so_state & SS_NOFDREF) == 0 || so->so_count != 0 || - (so->so_state & SS_PROTOREF) || (so->so_qstate & SQ_COMP)) { + (so->so_state & SS_PROTOREF) || (so->so_qstate == SQ_COMP)) { SOCK_UNLOCK(so); - ACCEPT_UNLOCK(); return; } - head = so->so_head; - if (head != NULL) { - KASSERT((so->so_qstate & SQ_COMP) != 0 || - (so->so_qstate & SQ_INCOMP) != 0, - ("sofree: so_head != NULL, but neither SQ_COMP nor " - "SQ_INCOMP")); - KASSERT((so->so_qstate & SQ_COMP) == 0 || - (so->so_qstate & SQ_INCOMP) == 0, - ("sofree: so->so_qstate is SQ_COMP and also SQ_INCOMP")); - TAILQ_REMOVE(&head->so_incomp, so, so_list); - head->so_incqlen--; - so->so_qstate &= ~SQ_INCOMP; - so->so_head = NULL; - } - KASSERT((so->so_qstate & SQ_COMP) == 0 && - (so->so_qstate & SQ_INCOMP) == 0, - ("sofree: so_head == NULL, but still SQ_COMP(%d) or SQ_INCOMP(%d)", - so->so_qstate & SQ_COMP, so->so_qstate & SQ_INCOMP)); - if (so->so_options & SO_ACCEPTCONN) { - KASSERT((TAILQ_EMPTY(&so->so_comp)), - ("sofree: so_comp populated")); - KASSERT((TAILQ_EMPTY(&so->so_incomp)), - ("sofree: so_incomp populated")); + if (!SOLISTENING(so) && so->so_qstate == SQ_INCOMP) { + struct socket *sol; + + sol = so->so_listen; + KASSERT(sol, ("%s: so %p on incomp of NULL", __func__, so)); + + /* + * To solve race between close of a listening socket and + * a socket on its incomplete queue, we need to lock both. + * The order is first listening socket, then regular. + * Since we don't have SS_NOFDREF neither SS_PROTOREF, this + * function and the listening socket are the only pointers + * to so. To preserve so and sol, we reference both and then + * relock. + * After relock the socket may not move to so_comp since it + * doesn't have PCB already, but it may be removed from + * so_incomp. If that happens, we share responsiblity on + * freeing the socket, but soclose() has already removed + * it from queue. + */ + soref(sol); + soref(so); + SOCK_UNLOCK(so); + SOLISTEN_LOCK(sol); + SOCK_LOCK(so); + if (so->so_qstate == SQ_INCOMP) { + KASSERT(so->so_listen == sol, + ("%s: so %p migrated out of sol %p", + __func__, so, sol)); + TAILQ_REMOVE(&sol->sol_incomp, so, so_list); + sol->sol_incqlen--; + /* This is guarenteed not to be the last. */ + refcount_release(&sol->so_count); + so->so_qstate = SQ_NONE; + so->so_listen = NULL; + } else + KASSERT(so->so_listen == NULL, + ("%s: so %p not on (in)comp with so_listen", + __func__, so)); + sorele(sol); + KASSERT(so->so_count == 1, + ("%s: so %p count %u", __func__, so, so->so_count)); + so->so_count = 0; } + if (SOLISTENING(so)) + so->so_error = ECONNABORTED; SOCK_UNLOCK(so); - ACCEPT_UNLOCK(); VNET_SO_ASSERT(so); if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose != NULL) @@ -858,12 +1069,14 @@ sofree(struct socket *so) * before calling pru_detach. This means that protocols shold not * assume they can perform socket wakeups, etc, in their detach code. */ - sbdestroy(&so->so_snd, so); - sbdestroy(&so->so_rcv, so); - seldrain(&so->so_snd.sb_sel); - seldrain(&so->so_rcv.sb_sel); - knlist_destroy(&so->so_rcv.sb_sel.si_note); - knlist_destroy(&so->so_snd.sb_sel.si_note); + if (!SOLISTENING(so)) { + sbdestroy(&so->so_snd, so); + sbdestroy(&so->so_rcv, so); + } + seldrain(&so->so_rdsel); + seldrain(&so->so_wrsel); + knlist_destroy(&so->so_rdsel.si_note); + knlist_destroy(&so->so_wrsel.si_note); sodealloc(so); } @@ -878,6 +1091,8 @@ sofree(struct socket *so) int soclose(struct socket *so) { + struct accept_queue lqueue; + bool listening; int error = 0; KASSERT(!(so->so_state & SS_NOFDREF), ("soclose: SS_NOFDREF on enter")); @@ -910,41 +1125,42 @@ soclose(struct socket *so) drop: if (so->so_proto->pr_usrreqs->pru_close != NULL) (*so->so_proto->pr_usrreqs->pru_close)(so); - ACCEPT_LOCK(); - if (so->so_options & SO_ACCEPTCONN) { + + SOCK_LOCK(so); + if ((listening = (so->so_options & SO_ACCEPTCONN))) { struct socket *sp; - /* - * Prevent new additions to the accept queues due - * to ACCEPT_LOCK races while we are draining them. - */ - so->so_options &= ~SO_ACCEPTCONN; - while ((sp = TAILQ_FIRST(&so->so_incomp)) != NULL) { - TAILQ_REMOVE(&so->so_incomp, sp, so_list); - so->so_incqlen--; - sp->so_qstate &= ~SQ_INCOMP; - sp->so_head = NULL; - ACCEPT_UNLOCK(); - soabort(sp); - ACCEPT_LOCK(); - } - while ((sp = TAILQ_FIRST(&so->so_comp)) != NULL) { - TAILQ_REMOVE(&so->so_comp, sp, so_list); - so->so_qlen--; - sp->so_qstate &= ~SQ_COMP; - sp->so_head = NULL; - ACCEPT_UNLOCK(); - soabort(sp); - ACCEPT_LOCK(); + + TAILQ_INIT(&lqueue); + TAILQ_SWAP(&lqueue, &so->sol_incomp, socket, so_list); + TAILQ_CONCAT(&lqueue, &so->sol_comp, so_list); + + so->sol_qlen = so->sol_incqlen = 0; + + TAILQ_FOREACH(sp, &lqueue, so_list) { + SOCK_LOCK(sp); + sp->so_qstate = SQ_NONE; + sp->so_listen = NULL; + SOCK_UNLOCK(sp); + /* Guaranteed not to be the last. */ + refcount_release(&so->so_count); } - KASSERT((TAILQ_EMPTY(&so->so_comp)), - ("%s: so_comp populated", __func__)); - KASSERT((TAILQ_EMPTY(&so->so_incomp)), - ("%s: so_incomp populated", __func__)); } - SOCK_LOCK(so); KASSERT((so->so_state & SS_NOFDREF) == 0, ("soclose: NOFDREF")); so->so_state |= SS_NOFDREF; - sorele(so); /* NB: Returns with ACCEPT_UNLOCK(). */ + sorele(so); + if (listening) { + struct socket *sp; + + TAILQ_FOREACH(sp, &lqueue, so_list) { + SOCK_LOCK(sp); + if (sp->so_count == 0) { + SOCK_UNLOCK(sp); + soabort(sp); + } else + /* sp is now in sofree() */ + SOCK_UNLOCK(sp); + } + } CURVNET_RESTORE(); return (error); } @@ -976,13 +1192,11 @@ soabort(struct socket *so) KASSERT(so->so_count == 0, ("soabort: so_count")); KASSERT((so->so_state & SS_PROTOREF) == 0, ("soabort: SS_PROTOREF")); KASSERT(so->so_state & SS_NOFDREF, ("soabort: !SS_NOFDREF")); - KASSERT((so->so_state & SQ_COMP) == 0, ("soabort: SQ_COMP")); - KASSERT((so->so_state & SQ_INCOMP) == 0, ("soabort: SQ_INCOMP")); + KASSERT(so->so_qstate == SQ_NONE, ("soabort: !SQ_NONE")); VNET_SO_ASSERT(so); if (so->so_proto->pr_usrreqs->pru_abort != NULL) (*so->so_proto->pr_usrreqs->pru_abort)(so); - ACCEPT_LOCK(); SOCK_LOCK(so); sofree(so); } @@ -1431,8 +1645,14 @@ sosend(struct socket *so, struct sockaddr *addr, struct uio *uio, int error; CURVNET_SET(so->so_vnet); - error = so->so_proto->pr_usrreqs->pru_sosend(so, addr, uio, top, - control, flags, td); + if (!SOLISTENING(so)) + error = so->so_proto->pr_usrreqs->pru_sosend(so, addr, uio, + top, control, flags, td); + else { + m_freem(top); + m_freem(control); + error = ENOTCONN; + } CURVNET_RESTORE(); return (error); } @@ -2368,8 +2588,11 @@ soreceive(struct socket *so, struct sockaddr **psa, struct uio *uio, int error; CURVNET_SET(so->so_vnet); - error = (so->so_proto->pr_usrreqs->pru_soreceive(so, psa, uio, mp0, - controlp, flagsp)); + if (!SOLISTENING(so)) + error = (so->so_proto->pr_usrreqs->pru_soreceive(so, psa, uio, + mp0, controlp, flagsp)); + else + error = ENOTCONN; CURVNET_RESTORE(); return (error); } @@ -2565,7 +2788,7 @@ sosetopt(struct socket *so, struct sockopt *sopt) } else { switch (sopt->sopt_name) { case SO_ACCEPTFILTER: - error = do_setopt_accept_filter(so, sopt); + error = accept_filt_setopt(so, sopt); if (error) goto bad; break; @@ -2653,38 +2876,7 @@ sosetopt(struct socket *so, struct sockopt *sopt) goto bad; } - switch (sopt->sopt_name) { - case SO_SNDBUF: - case SO_RCVBUF: - if (sbreserve(sopt->sopt_name == SO_SNDBUF ? - &so->so_snd : &so->so_rcv, (u_long)optval, - so, curthread) == 0) { - error = ENOBUFS; - goto bad; - } - (sopt->sopt_name == SO_SNDBUF ? &so->so_snd : - &so->so_rcv)->sb_flags &= ~SB_AUTOSIZE; - break; - - /* - * Make sure the low-water is never greater than the - * high-water. - */ - case SO_SNDLOWAT: - SOCKBUF_LOCK(&so->so_snd); - so->so_snd.sb_lowat = - (optval > so->so_snd.sb_hiwat) ? - so->so_snd.sb_hiwat : optval; - SOCKBUF_UNLOCK(&so->so_snd); - break; - case SO_RCVLOWAT: - SOCKBUF_LOCK(&so->so_rcv); - so->so_rcv.sb_lowat = - (optval > so->so_rcv.sb_hiwat) ? - so->so_rcv.sb_hiwat : optval; - SOCKBUF_UNLOCK(&so->so_rcv); - break; - } + error = sbsetopt(so, sopt->sopt_name, optval); break; case SO_SNDTIMEO: @@ -2825,7 +3017,7 @@ sogetopt(struct socket *so, struct sockopt *sopt) } else { switch (sopt->sopt_name) { case SO_ACCEPTFILTER: - error = do_getopt_accept_filter(so, sopt); + error = accept_filt_getopt(so, sopt); break; case SO_LINGER: @@ -2869,19 +3061,23 @@ integer: goto integer; case SO_SNDBUF: - optval = so->so_snd.sb_hiwat; + optval = SOLISTENING(so) ? so->sol_sbsnd_hiwat : + so->so_snd.sb_hiwat; goto integer; case SO_RCVBUF: - optval = so->so_rcv.sb_hiwat; + optval = SOLISTENING(so) ? so->sol_sbrcv_hiwat : + so->so_rcv.sb_hiwat; goto integer; case SO_SNDLOWAT: - optval = so->so_snd.sb_lowat; + optval = SOLISTENING(so) ? so->sol_sbsnd_lowat : + so->so_snd.sb_lowat; goto integer; case SO_RCVLOWAT: - optval = so->so_rcv.sb_lowat; + optval = SOLISTENING(so) ? so->sol_sbrcv_lowat : + so->so_rcv.sb_lowat; goto integer; case SO_SNDTIMEO: @@ -2933,15 +3129,15 @@ integer: break; case SO_LISTENQLIMIT: - optval = so->so_qlimit; + optval = SOLISTENING(so) ? so->sol_qlimit : 0; goto integer; case SO_LISTENQLEN: - optval = so->so_qlen; + optval = SOLISTENING(so) ? so->sol_qlen : 0; goto integer; case SO_LISTENINCQLEN: - optval = so->so_incqlen; + optval = SOLISTENING(so) ? so->sol_incqlen : 0; goto integer; case SO_TS_CLOCK: @@ -3092,7 +3288,7 @@ sohasoutofband(struct socket *so) if (so->so_sigio != NULL) pgsigio(&so->so_sigio, SIGURG, 0); #endif /* __rtems__ */ - selwakeuppri(&so->so_rcv.sb_sel, PSOCK); + selwakeuppri(&so->so_rdsel, PSOCK); } int @@ -3112,44 +3308,54 @@ int sopoll_generic(struct socket *so, int events, struct ucred *active_cred, struct thread *td) { - int revents = 0; - - SOCKBUF_LOCK(&so->so_snd); - SOCKBUF_LOCK(&so->so_rcv); - if (events & (POLLIN | POLLRDNORM)) - if (soreadabledata(so)) - revents |= events & (POLLIN | POLLRDNORM); + int revents; - if (events & (POLLOUT | POLLWRNORM)) - if (sowriteable(so)) - revents |= events & (POLLOUT | POLLWRNORM); - - if (events & (POLLPRI | POLLRDBAND)) - if (so->so_oobmark || (so->so_rcv.sb_state & SBS_RCVATMARK)) - revents |= events & (POLLPRI | POLLRDBAND); - - if ((events & POLLINIGNEOF) == 0) { - if (so->so_rcv.sb_state & SBS_CANTRCVMORE) { - revents |= events & (POLLIN | POLLRDNORM); - if (so->so_snd.sb_state & SBS_CANTSENDMORE) - revents |= POLLHUP; + SOCK_LOCK(so); + if (SOLISTENING(so)) { + if (!(events & (POLLIN | POLLRDNORM))) + revents = 0; + else if (!TAILQ_EMPTY(&so->sol_comp)) + revents = events & (POLLIN | POLLRDNORM); + else { + selrecord(td, &so->so_rdsel); + revents = 0; } - } - - if (revents == 0) { - if (events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) { - selrecord(td, &so->so_rcv.sb_sel); - so->so_rcv.sb_flags |= SB_SEL; + } else { + revents = 0; + SOCKBUF_LOCK(&so->so_snd); + SOCKBUF_LOCK(&so->so_rcv); + if (events & (POLLIN | POLLRDNORM)) + if (soreadabledata(so)) + revents |= events & (POLLIN | POLLRDNORM); + if (events & (POLLOUT | POLLWRNORM)) + if (sowriteable(so)) + revents |= events & (POLLOUT | POLLWRNORM); + if (events & (POLLPRI | POLLRDBAND)) + if (so->so_oobmark || + (so->so_rcv.sb_state & SBS_RCVATMARK)) + revents |= events & (POLLPRI | POLLRDBAND); + if ((events & POLLINIGNEOF) == 0) { + if (so->so_rcv.sb_state & SBS_CANTRCVMORE) { + revents |= events & (POLLIN | POLLRDNORM); + if (so->so_snd.sb_state & SBS_CANTSENDMORE) + revents |= POLLHUP; + } } - - if (events & (POLLOUT | POLLWRNORM)) { - selrecord(td, &so->so_snd.sb_sel); - so->so_snd.sb_flags |= SB_SEL; + if (revents == 0) { + if (events & + (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) { + selrecord(td, &so->so_rdsel); + so->so_rcv.sb_flags |= SB_SEL; + } + if (events & (POLLOUT | POLLWRNORM)) { + selrecord(td, &so->so_wrsel); + so->so_snd.sb_flags |= SB_SEL; + } } + SOCKBUF_UNLOCK(&so->so_rcv); + SOCKBUF_UNLOCK(&so->so_snd); } - - SOCKBUF_UNLOCK(&so->so_rcv); - SOCKBUF_UNLOCK(&so->so_snd); + SOCK_UNLOCK(so); return (revents); } @@ -3158,28 +3364,38 @@ soo_kqfilter(struct file *fp, struct knote *kn) { struct socket *so = kn->kn_fp->f_data; struct sockbuf *sb; + struct knlist *knl; switch (kn->kn_filter) { case EVFILT_READ: kn->kn_fop = &soread_filtops; + knl = &so->so_rdsel.si_note; sb = &so->so_rcv; break; case EVFILT_WRITE: kn->kn_fop = &sowrite_filtops; + knl = &so->so_wrsel.si_note; sb = &so->so_snd; break; case EVFILT_EMPTY: kn->kn_fop = &soempty_filtops; + knl = &so->so_wrsel.si_note; sb = &so->so_snd; break; default: return (EINVAL); } - SOCKBUF_LOCK(sb); - knlist_add(&sb->sb_sel.si_note, kn, 1); - sb->sb_flags |= SB_KNOTE; - SOCKBUF_UNLOCK(sb); + SOCK_LOCK(so); + if (SOLISTENING(so)) { + knlist_add(knl, kn, 1); + } else { + SOCKBUF_LOCK(sb); + knlist_add(knl, kn, 1); + sb->sb_flags |= SB_KNOTE; + SOCKBUF_UNLOCK(sb); + } + SOCK_UNLOCK(so); return (0); } #ifdef __rtems__ @@ -3367,11 +3583,11 @@ filt_sordetach(struct knote *kn) { struct socket *so = kn->kn_fp->f_data; - SOCKBUF_LOCK(&so->so_rcv); - knlist_remove(&so->so_rcv.sb_sel.si_note, kn, 1); - if (knlist_empty(&so->so_rcv.sb_sel.si_note)) + so_rdknl_lock(so); + knlist_remove(&so->so_rdsel.si_note, kn, 1); + if (!SOLISTENING(so) && knlist_empty(&so->so_rdsel.si_note)) so->so_rcv.sb_flags &= ~SB_KNOTE; - SOCKBUF_UNLOCK(&so->so_rcv); + so_rdknl_unlock(so); } /*ARGSUSED*/ @@ -3381,11 +3597,13 @@ filt_soread(struct knote *kn, long hint) struct socket *so; so = kn->kn_fp->f_data; - if (so->so_options & SO_ACCEPTCONN) { - kn->kn_data = so->so_qlen; - return (!TAILQ_EMPTY(&so->so_comp)); + if (SOLISTENING(so)) { + SOCK_LOCK_ASSERT(so); + kn->kn_data = so->sol_qlen; + return (!TAILQ_EMPTY(&so->sol_comp)); } + SOCKBUF_LOCK_ASSERT(&so->so_rcv); kn->kn_data = sbavail(&so->so_rcv) - so->so_rcv.sb_ctl; @@ -3411,11 +3629,11 @@ filt_sowdetach(struct knote *kn) { struct socket *so = kn->kn_fp->f_data; - SOCKBUF_LOCK(&so->so_snd); - knlist_remove(&so->so_snd.sb_sel.si_note, kn, 1); - if (knlist_empty(&so->so_snd.sb_sel.si_note)) + so_wrknl_lock(so); + knlist_remove(&so->so_wrsel.si_note, kn, 1); + if (!SOLISTENING(so) && knlist_empty(&so->so_wrsel.si_note)) so->so_snd.sb_flags &= ~SB_KNOTE; - SOCKBUF_UNLOCK(&so->so_snd); + so_wrknl_unlock(so); } /*ARGSUSED*/ @@ -3425,6 +3643,10 @@ filt_sowrite(struct knote *kn, long hint) struct socket *so; so = kn->kn_fp->f_data; + + if (SOLISTENING(so)) + return (0); + SOCKBUF_LOCK_ASSERT(&so->so_snd); kn->kn_data = sbspace(&so->so_snd); @@ -3451,6 +3673,10 @@ filt_soempty(struct knote *kn, long hint) struct socket *so; so = kn->kn_fp->f_data; + + if (SOLISTENING(so)) + return (1); + SOCKBUF_LOCK_ASSERT(&so->so_snd); kn->kn_data = sbused(&so->so_snd); @@ -3521,42 +3747,52 @@ soisconnected(struct socket *so) struct socket *head; int ret; + /* + * XXXGL: this is the only place where we acquire socket locks + * in reverse order: first child, then listening socket. To + * avoid possible LOR, use try semantics. + */ restart: - ACCEPT_LOCK(); SOCK_LOCK(so); + if ((head = so->so_listen) != NULL && + __predict_false(SOLISTEN_TRYLOCK(head) == 0)) { + SOCK_UNLOCK(so); + goto restart; + } so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING|SS_ISCONFIRMING); so->so_state |= SS_ISCONNECTED; - head = so->so_head; - if (head != NULL && (so->so_qstate & SQ_INCOMP)) { + if (head != NULL && (so->so_qstate == SQ_INCOMP)) { +again: if ((so->so_options & SO_ACCEPTFILTER) == 0) { + TAILQ_REMOVE(&head->sol_incomp, so, so_list); + head->sol_incqlen--; + TAILQ_INSERT_TAIL(&head->sol_comp, so, so_list); + head->sol_qlen++; + so->so_qstate = SQ_COMP; SOCK_UNLOCK(so); - TAILQ_REMOVE(&head->so_incomp, so, so_list); - head->so_incqlen--; - so->so_qstate &= ~SQ_INCOMP; - TAILQ_INSERT_TAIL(&head->so_comp, so, so_list); - head->so_qlen++; - so->so_qstate |= SQ_COMP; - ACCEPT_UNLOCK(); - sorwakeup(head); - wakeup_one(&head->so_timeo); + solisten_wakeup(head); /* unlocks */ } else { - ACCEPT_UNLOCK(); + SOCKBUF_LOCK(&so->so_rcv); soupcall_set(so, SO_RCV, - head->so_accf->so_accept_filter->accf_callback, - head->so_accf->so_accept_filter_arg); + head->sol_accept_filter->accf_callback, + head->sol_accept_filter_arg); so->so_options &= ~SO_ACCEPTFILTER; - ret = head->so_accf->so_accept_filter->accf_callback(so, - head->so_accf->so_accept_filter_arg, M_NOWAIT); - if (ret == SU_ISCONNECTED) + ret = head->sol_accept_filter->accf_callback(so, + head->sol_accept_filter_arg, M_NOWAIT); + if (ret == SU_ISCONNECTED) { soupcall_clear(so, SO_RCV); + SOCKBUF_UNLOCK(&so->so_rcv); + goto again; + } + SOCKBUF_UNLOCK(&so->so_rcv); SOCK_UNLOCK(so); - if (ret == SU_ISCONNECTED) - goto restart; + SOLISTEN_UNLOCK(head); } return; } + if (head != NULL) + SOLISTEN_UNLOCK(head); SOCK_UNLOCK(so); - ACCEPT_UNLOCK(); wakeup(&so->so_timeo); sorwakeup(so); sowwakeup(so); @@ -3566,16 +3802,17 @@ void soisdisconnecting(struct socket *so) { - /* - * Note: This code assumes that SOCK_LOCK(so) and - * SOCKBUF_LOCK(&so->so_rcv) are the same. - */ - SOCKBUF_LOCK(&so->so_rcv); + SOCK_LOCK(so); so->so_state &= ~SS_ISCONNECTING; so->so_state |= SS_ISDISCONNECTING; - socantrcvmore_locked(so); - SOCKBUF_LOCK(&so->so_snd); - socantsendmore_locked(so); + + if (!SOLISTENING(so)) { + SOCKBUF_LOCK(&so->so_rcv); + socantrcvmore_locked(so); + SOCKBUF_LOCK(&so->so_snd); + socantsendmore_locked(so); + } + SOCK_UNLOCK(so); wakeup(&so->so_timeo); } @@ -3583,17 +3820,18 @@ void soisdisconnected(struct socket *so) { - /* - * Note: This code assumes that SOCK_LOCK(so) and - * SOCKBUF_LOCK(&so->so_rcv) are the same. - */ - SOCKBUF_LOCK(&so->so_rcv); + SOCK_LOCK(so); so->so_state &= ~(SS_ISCONNECTING|SS_ISCONNECTED|SS_ISDISCONNECTING); so->so_state |= SS_ISDISCONNECTED; - socantrcvmore_locked(so); - SOCKBUF_LOCK(&so->so_snd); - sbdrop_locked(&so->so_snd, sbused(&so->so_snd)); - socantsendmore_locked(so); + + if (!SOLISTENING(so)) { + SOCKBUF_LOCK(&so->so_rcv); + socantrcvmore_locked(so); + SOCKBUF_LOCK(&so->so_snd); + sbdrop_locked(&so->so_snd, sbused(&so->so_snd)); + socantsendmore_locked(so); + } + SOCK_UNLOCK(so); wakeup(&so->so_timeo); } @@ -3615,11 +3853,12 @@ sodupsockaddr(const struct sockaddr *sa, int mflags) * Register per-socket buffer upcalls. */ void -soupcall_set(struct socket *so, int which, - int (*func)(struct socket *, void *, int), void *arg) +soupcall_set(struct socket *so, int which, so_upcall_t func, void *arg) { struct sockbuf *sb; + KASSERT(!SOLISTENING(so), ("%s: so %p listening", __func__, so)); + switch (which) { case SO_RCV: sb = &so->so_rcv; @@ -3631,10 +3870,6 @@ soupcall_set(struct socket *so, int which, panic("soupcall_set: bad which"); } SOCKBUF_LOCK_ASSERT(sb); -#if 0 - /* XXX: accf_http actually wants to do this on purpose. */ - KASSERT(sb->sb_upcall == NULL, ("soupcall_set: overwriting upcall")); -#endif sb->sb_upcall = func; sb->sb_upcallarg = arg; sb->sb_flags |= SB_UPCALL; @@ -3645,6 +3880,8 @@ soupcall_clear(struct socket *so, int which) { struct sockbuf *sb; + KASSERT(!SOLISTENING(so), ("%s: so %p listening", __func__, so)); + switch (which) { case SO_RCV: sb = &so->so_rcv; @@ -3656,12 +3893,110 @@ soupcall_clear(struct socket *so, int which) panic("soupcall_clear: bad which"); } SOCKBUF_LOCK_ASSERT(sb); - KASSERT(sb->sb_upcall != NULL, ("soupcall_clear: no upcall to clear")); + KASSERT(sb->sb_upcall != NULL, + ("%s: so %p no upcall to clear", __func__, so)); sb->sb_upcall = NULL; sb->sb_upcallarg = NULL; sb->sb_flags &= ~SB_UPCALL; } +void +solisten_upcall_set(struct socket *so, so_upcall_t func, void *arg) +{ + + SOLISTEN_LOCK_ASSERT(so); + so->sol_upcall = func; + so->sol_upcallarg = arg; +} + +static void +so_rdknl_lock(void *arg) +{ + struct socket *so = arg; + + if (SOLISTENING(so)) + SOCK_LOCK(so); + else + SOCKBUF_LOCK(&so->so_rcv); +} + +static void +so_rdknl_unlock(void *arg) +{ + struct socket *so = arg; + + if (SOLISTENING(so)) + SOCK_UNLOCK(so); + else + SOCKBUF_UNLOCK(&so->so_rcv); +} + +static void +so_rdknl_assert_locked(void *arg) +{ + struct socket *so = arg; + + if (SOLISTENING(so)) + SOCK_LOCK_ASSERT(so); + else + SOCKBUF_LOCK_ASSERT(&so->so_rcv); +} + +static void +so_rdknl_assert_unlocked(void *arg) +{ + struct socket *so = arg; + + if (SOLISTENING(so)) + SOCK_UNLOCK_ASSERT(so); + else + SOCKBUF_UNLOCK_ASSERT(&so->so_rcv); +} + +static void +so_wrknl_lock(void *arg) +{ + struct socket *so = arg; + + if (SOLISTENING(so)) + SOCK_LOCK(so); + else + SOCKBUF_LOCK(&so->so_snd); +} + +static void +so_wrknl_unlock(void *arg) +{ + struct socket *so = arg; + + if (SOLISTENING(so)) + SOCK_UNLOCK(so); + else + SOCKBUF_UNLOCK(&so->so_snd); +} + +static void +so_wrknl_assert_locked(void *arg) +{ + struct socket *so = arg; + + if (SOLISTENING(so)) + SOCK_LOCK_ASSERT(so); + else + SOCKBUF_LOCK_ASSERT(&so->so_snd); +} + +static void +so_wrknl_assert_unlocked(void *arg) +{ + struct socket *so = arg; + + if (SOLISTENING(so)) + SOCK_UNLOCK_ASSERT(so); + else + SOCKBUF_UNLOCK_ASSERT(&so->so_snd); +} + /* * Create an external-format (``xsocket'') structure using the information in * the kernel-format socket structure pointed to by so. This is done to @@ -3683,36 +4018,28 @@ sotoxsocket(struct socket *so, struct xsocket *xso) xso->so_pcb = so->so_pcb; xso->xso_protocol = so->so_proto->pr_protocol; xso->xso_family = so->so_proto->pr_domain->dom_family; - xso->so_qlen = so->so_qlen; - xso->so_incqlen = so->so_incqlen; - xso->so_qlimit = so->so_qlimit; xso->so_timeo = so->so_timeo; xso->so_error = so->so_error; - xso->so_pgid = so->so_sigio ? so->so_sigio->sio_pgid : 0; - xso->so_oobmark = so->so_oobmark; - sbtoxsockbuf(&so->so_snd, &xso->so_snd); - sbtoxsockbuf(&so->so_rcv, &xso->so_rcv); #ifndef __rtems__ xso->so_uid = so->so_cred->cr_uid; #else /* __rtems__ */ xso->so_uid = BSD_DEFAULT_UID; #endif /* __rtems__ */ -} - - -/* - * Socket accessor functions to provide external consumers with - * a safe interface to socket state - * - */ - -void -so_listeners_apply_all(struct socket *so, void (*func)(struct socket *, void *), - void *arg) -{ - - TAILQ_FOREACH(so, &so->so_comp, so_list) - func(so, arg); + xso->so_pgid = so->so_sigio ? so->so_sigio->sio_pgid : 0; + if (SOLISTENING(so)) { + xso->so_qlen = so->sol_qlen; + xso->so_incqlen = so->sol_incqlen; + xso->so_qlimit = so->sol_qlimit; + xso->so_oobmark = 0; + bzero(&xso->so_snd, sizeof(xso->so_snd)); + bzero(&xso->so_rcv, sizeof(xso->so_rcv)); + } else { + xso->so_state |= so->so_qstate; + xso->so_qlen = xso->so_incqlen = xso->so_qlimit = 0; + xso->so_oobmark = so->so_oobmark; + sbtoxsockbuf(&so->so_snd, &xso->so_snd); + sbtoxsockbuf(&so->so_rcv, &xso->so_rcv); + } } struct sockbuf * diff --git a/freebsd/sys/kern/uipc_syscalls.c b/freebsd/sys/kern/uipc_syscalls.c index f301c12c..5a9a381f 100644 --- a/freebsd/sys/kern/uipc_syscalls.c +++ b/freebsd/sys/kern/uipc_syscalls.c @@ -70,13 +70,6 @@ __FBSDID("$FreeBSD$"); #include <security/audit/audit.h> #include <security/mac/mac_framework.h> -/* - * Flags for accept1() and kern_accept4(), in addition to SOCK_CLOEXEC - * and SOCK_NONBLOCK. - */ -#define ACCEPT4_INHERIT 0x1 -#define ACCEPT4_COMPAT 0x2 - static int sendit(struct thread *td, int s, struct msghdr *mp, int flags); static int recvit(struct thread *td, int s, struct msghdr *mp, void *namelenp); @@ -524,59 +517,22 @@ kern_accept4(struct thread *td, int s, struct sockaddr **name, (flags & SOCK_CLOEXEC) ? O_CLOEXEC : 0, &fcaps); if (error != 0) goto done; - ACCEPT_LOCK(); - if ((head->so_state & SS_NBIO) && TAILQ_EMPTY(&head->so_comp)) { - ACCEPT_UNLOCK(); - error = EWOULDBLOCK; - goto noconnection; - } - while (TAILQ_EMPTY(&head->so_comp) && head->so_error == 0) { - if (head->so_rcv.sb_state & SBS_CANTRCVMORE) { - head->so_error = ECONNABORTED; - break; - } - error = msleep(&head->so_timeo, &accept_mtx, PSOCK | PCATCH, - "accept", 0); - if (error != 0) { - ACCEPT_UNLOCK(); - goto noconnection; - } - } - if (head->so_error) { - error = head->so_error; - head->so_error = 0; - ACCEPT_UNLOCK(); + SOCK_LOCK(head); + if (!SOLISTENING(head)) { + SOCK_UNLOCK(head); + error = EINVAL; goto noconnection; } - so = TAILQ_FIRST(&head->so_comp); - KASSERT(!(so->so_qstate & SQ_INCOMP), ("accept1: so SQ_INCOMP")); - KASSERT(so->so_qstate & SQ_COMP, ("accept1: so not SQ_COMP")); - /* - * Before changing the flags on the socket, we have to bump the - * reference count. Otherwise, if the protocol calls sofree(), - * the socket will be released due to a zero refcount. - */ - SOCK_LOCK(so); /* soref() and so_state update */ - soref(so); /* file descriptor reference */ - - TAILQ_REMOVE(&head->so_comp, so, so_list); - head->so_qlen--; - if (flags & ACCEPT4_INHERIT) - so->so_state |= (head->so_state & SS_NBIO); - else - so->so_state |= (flags & SOCK_NONBLOCK) ? SS_NBIO : 0; - so->so_qstate &= ~SQ_COMP; - so->so_head = NULL; - - SOCK_UNLOCK(so); - ACCEPT_UNLOCK(); + error = solisten_dequeue(head, &so, flags); + if (error != 0) + goto noconnection; /* An extra reference on `nfp' has been held for us by falloc(). */ td->td_retval[0] = fd; - /* connection has been removed from the listen queue */ - KNOTE_UNLOCKED(&head->so_rcv.sb_sel.si_note, 0); + /* Connection has been removed from the listen queue. */ + KNOTE_UNLOCKED(&head->so_rdsel.si_note, 0); if (flags & ACCEPT4_INHERIT) { pgid = fgetown(&head->so_sigio); @@ -594,7 +550,6 @@ kern_accept4(struct thread *td, int s, struct sockaddr **name, (void) fo_ioctl(nfp, FIONBIO, &tmp, td->td_ucred, td); tmp = fflag & FASYNC; (void) fo_ioctl(nfp, FIOASYNC, &tmp, td->td_ucred, td); - sa = NULL; error = soaccept(so, &sa); if (error != 0) goto noconnection; @@ -769,7 +724,7 @@ kern_connectat(struct thread *td, int dirfd, int fd, struct sockaddr *sa) } SOCK_LOCK(so); while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) { - error = msleep(&so->so_timeo, SOCK_MTX(so), PSOCK | PCATCH, + error = msleep(&so->so_timeo, &so->so_lock, PSOCK | PCATCH, "connec", 0); if (error != 0) { if (error == EINTR || error == ERESTART) diff --git a/freebsd/sys/kern/uipc_usrreq.c b/freebsd/sys/kern/uipc_usrreq.c index 8e60f227..7237956a 100644 --- a/freebsd/sys/kern/uipc_usrreq.c +++ b/freebsd/sys/kern/uipc_usrreq.c @@ -202,10 +202,9 @@ SYSCTL_INT(_net_local, OID_AUTO, deferred, CTLFLAG_RD, /* * Locking and synchronization: * - * Three types of locks exit in the local domain socket implementation: a - * global list mutex, a global linkage rwlock, and per-unpcb mutexes. Of the - * global locks, the list lock protects the socket count, global generation - * number, and stream/datagram global lists. The linkage lock protects the + * Two types of locks exist in the local domain socket implementation: a + * a global linkage rwlock and per-unpcb mutexes. The linkage lock protects + * the socket count, global generation number, stream/datagram global lists and * interconnection of unpcbs, the v_socket and unp_vnode pointers, and can be * held exclusively over the acquisition of multiple unpcb locks to prevent * deadlock. @@ -246,7 +245,6 @@ SYSCTL_INT(_net_local, OID_AUTO, deferred, CTLFLAG_RD, * to perform namei() and other file system operations. */ static struct rwlock unp_link_rwlock; -static struct mtx unp_list_lock; static struct mtx unp_defers_lock; #define UNP_LINK_LOCK_INIT() rw_init(&unp_link_rwlock, \ @@ -263,11 +261,7 @@ static struct mtx unp_defers_lock; #define UNP_LINK_WUNLOCK() rw_wunlock(&unp_link_rwlock) #define UNP_LINK_WLOCK_ASSERT() rw_assert(&unp_link_rwlock, \ RA_WLOCKED) - -#define UNP_LIST_LOCK_INIT() mtx_init(&unp_list_lock, \ - "unp_list_lock", NULL, MTX_DEF) -#define UNP_LIST_LOCK() mtx_lock(&unp_list_lock) -#define UNP_LIST_UNLOCK() mtx_unlock(&unp_list_lock) +#define UNP_LINK_WOWNED() rw_wowned(&unp_link_rwlock) #define UNP_DEFERRED_LOCK_INIT() mtx_init(&unp_defers_lock, \ "unp_defer", NULL, MTX_DEF) @@ -417,6 +411,7 @@ uipc_attach(struct socket *so, int proto, struct thread *td) u_long sendspace, recvspace; struct unpcb *unp; int error; + bool locked; KASSERT(so->so_pcb == NULL, ("uipc_attach: so_pcb != NULL")); if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { @@ -451,10 +446,12 @@ uipc_attach(struct socket *so, int proto, struct thread *td) unp->unp_socket = so; so->so_pcb = unp; unp->unp_refcount = 1; - if (so->so_head != NULL) + if (so->so_listen != NULL) unp->unp_flags |= UNP_NASCENT; - UNP_LIST_LOCK(); + if ((locked = UNP_LINK_WOWNED()) == false) + UNP_LINK_WLOCK(); + unp->unp_gencnt = ++unp_gencnt; unp_count++; switch (so->so_type) { @@ -473,7 +470,9 @@ uipc_attach(struct socket *so, int proto, struct thread *td) default: panic("uipc_attach"); } - UNP_LIST_UNLOCK(); + + if (locked == false) + UNP_LINK_WUNLOCK(); return (0); } @@ -516,6 +515,14 @@ static const IMFS_node_control rtems_uipc_imfs_control = static const IMFS_node_control rtems_uipc_imfs_zombi_control = IMFS_GENERIC_INITIALIZER(&rtems_filesystem_handlers_default, NULL, IMFS_node_destroy_default); + +static void +VOP_UNP_DETACH(IMFS_generic_t *vp) +{ + + vp->Node.control = &rtems_uipc_imfs_zombi_control; + vp->context = NULL; +} #endif /* __rtems__ */ static int uipc_bindat(int fd, struct socket *so, struct sockaddr *nam, struct thread *td) @@ -630,7 +637,7 @@ restart: UNP_LINK_WLOCK(); UNP_PCB_LOCK(unp); #ifndef __rtems__ - VOP_UNP_BIND(vp, unp->unp_socket); + VOP_UNP_BIND(vp, unp); unp->unp_vnode = vp; #endif /* __rtems__ */ unp->unp_addr = soun; @@ -690,6 +697,11 @@ static void uipc_close(struct socket *so) { struct unpcb *unp, *unp2; +#ifndef __rtems__ + struct vnode *vp; +#else /* __rtems__ */ + IMFS_generic_t *vp; +#endif /* __rtems__ */ unp = sotounpcb(so); KASSERT(unp != NULL, ("uipc_close: unp == NULL")); @@ -702,8 +714,16 @@ uipc_close(struct socket *so) unp_disconnect(unp, unp2); UNP_PCB_UNLOCK(unp2); } + if (SOLISTENING(so) && ((vp = unp->unp_vnode) != NULL)) { + VOP_UNP_DETACH(vp); + unp->unp_vnode = NULL; + } UNP_PCB_UNLOCK(unp); UNP_LINK_WUNLOCK(); +#ifndef __rtems__ + if (vp) + vrele(vp); +#endif /* __rtems__ */ } static int @@ -747,29 +767,16 @@ uipc_detach(struct socket *so) local_unp_rights = 0; #endif /* __rtems__ */ - UNP_LIST_LOCK(); + UNP_LINK_WLOCK(); LIST_REMOVE(unp, unp_link); unp->unp_gencnt = ++unp_gencnt; --unp_count; - UNP_LIST_UNLOCK(); - - if ((unp->unp_flags & UNP_NASCENT) != 0) { - UNP_PCB_LOCK(unp); - goto teardown; - } - UNP_LINK_WLOCK(); UNP_PCB_LOCK(unp); + if ((unp->unp_flags & UNP_NASCENT) != 0) + goto teardown; - /* - * XXXRW: Should assert vp->v_socket == so. - */ if ((vp = unp->unp_vnode) != NULL) { -#ifndef __rtems__ VOP_UNP_DETACH(vp); -#else /* __rtems__ */ - vp->Node.control = &rtems_uipc_imfs_zombi_control; - vp->context = NULL; -#endif /* __rtems__ */ unp->unp_vnode = NULL; } unp2 = unp->unp_conn; @@ -793,8 +800,8 @@ uipc_detach(struct socket *so) #ifndef __rtems__ local_unp_rights = unp_rights; #endif /* __rtems__ */ - UNP_LINK_WUNLOCK(); teardown: + UNP_LINK_WUNLOCK(); unp->unp_socket->so_pcb = NULL; saved_unp_addr = unp->unp_addr; unp->unp_addr = NULL; @@ -860,7 +867,6 @@ uipc_listen(struct socket *so, int backlog, struct thread *td) error = solisten_proto_check(so); if (error == 0) { cru2x(td->td_ucred, &unp->unp_peercred); - unp->unp_flags |= UNP_HAVEPCCACHED; solisten_proto(so, backlog); } SOCK_UNLOCK(so); @@ -1439,7 +1445,7 @@ unp_connectat(int fd, struct socket *so, struct sockaddr *nam, #else /* __rtems__ */ struct IMFS_jnode_tt *vp; #endif /* __rtems__ */ - struct socket *so2, *so3; + struct socket *so2; struct unpcb *unp, *unp2, *unp3; #ifndef __rtems__ struct nameidata nd; @@ -1450,7 +1456,9 @@ unp_connectat(int fd, struct socket *so, struct sockaddr *nam, const rtems_filesystem_location_info_t *currentloc; #endif /* __rtems__ */ struct sockaddr *sa; +#ifndef __rtems__ cap_rights_t rights; +#endif /* __rtems__ */ int error, len; if (nam->sa_family != AF_UNIX) @@ -1535,34 +1543,38 @@ unp_connectat(int fd, struct socket *so, struct sockaddr *nam, */ UNP_LINK_WLOCK(); #ifndef __rtems__ - VOP_UNP_CONNECT(vp, &so2); + VOP_UNP_CONNECT(vp, &unp2); + if (unp2 == NULL) { + error = ECONNREFUSED; + goto bad2; + } + so2 = unp2->unp_socket; #else /* __rtems__ */ so2 = IMFS_generic_get_context_by_node(vp); -#endif /* __rtems__ */ if (so2 == NULL) { error = ECONNREFUSED; goto bad2; } + unp2 = sotounpcb(so2); +#endif /* __rtems__ */ if (so->so_type != so2->so_type) { error = EPROTOTYPE; goto bad2; } + UNP_PCB_LOCK(unp); + UNP_PCB_LOCK(unp2); if (so->so_proto->pr_flags & PR_CONNREQUIRED) { if (so2->so_options & SO_ACCEPTCONN) { CURVNET_SET(so2->so_vnet); - so3 = sonewconn(so2, 0); + so2 = sonewconn(so2, 0); CURVNET_RESTORE(); } else - so3 = NULL; - if (so3 == NULL) { + so2 = NULL; + if (so2 == NULL) { error = ECONNREFUSED; - goto bad2; + goto bad3; } - unp = sotounpcb(so); - unp2 = sotounpcb(so2); - unp3 = sotounpcb(so3); - UNP_PCB_LOCK(unp); - UNP_PCB_LOCK(unp2); + unp3 = sotounpcb(so2); UNP_PCB_LOCK(unp3); if (unp2->unp_addr != NULL) { bcopy(unp2->unp_addr, sa, unp2->unp_addr->sun_len); @@ -1583,30 +1595,24 @@ unp_connectat(int fd, struct socket *so, struct sockaddr *nam, * listen(); uipc_listen() cached that process's credentials * at that time so we can use them now. */ - KASSERT(unp2->unp_flags & UNP_HAVEPCCACHED, - ("unp_connect: listener without cached peercred")); memcpy(&unp->unp_peercred, &unp2->unp_peercred, sizeof(unp->unp_peercred)); unp->unp_flags |= UNP_HAVEPC; if (unp2->unp_flags & UNP_WANTCRED) unp3->unp_flags |= UNP_WANTCRED; - UNP_PCB_UNLOCK(unp3); UNP_PCB_UNLOCK(unp2); - UNP_PCB_UNLOCK(unp); + unp2 = unp3; #ifdef MAC - mac_socketpeer_set_from_socket(so, so3); - mac_socketpeer_set_from_socket(so3, so); + mac_socketpeer_set_from_socket(so, so2); + mac_socketpeer_set_from_socket(so2, so); #endif - - so2 = so3; } - unp = sotounpcb(so); - KASSERT(unp != NULL, ("unp_connect: unp == NULL")); - unp2 = sotounpcb(so2); - KASSERT(unp2 != NULL, ("unp_connect: unp2 == NULL")); - UNP_PCB_LOCK(unp); - UNP_PCB_LOCK(unp2); + + KASSERT(unp2 != NULL && so2 != NULL && unp2->unp_socket == so2 && + sotounpcb(so2) == unp2, + ("%s: unp2 %p so2 %p", __func__, unp2, so2)); error = unp_connect2(so, so2, PRU_CONNECT); +bad3: UNP_PCB_UNLOCK(unp2); UNP_PCB_UNLOCK(unp); bad2: @@ -1750,10 +1756,10 @@ unp_pcblist(SYSCTL_HANDLER_ARGS) * OK, now we're committed to doing something. */ xug = malloc(sizeof(*xug), M_TEMP, M_WAITOK); - UNP_LIST_LOCK(); + UNP_LINK_RLOCK(); gencnt = unp_gencnt; n = unp_count; - UNP_LIST_UNLOCK(); + UNP_LINK_RUNLOCK(); xug->xug_len = sizeof *xug; xug->xug_count = n; @@ -1767,7 +1773,7 @@ unp_pcblist(SYSCTL_HANDLER_ARGS) unp_list = malloc(n * sizeof *unp_list, M_TEMP, M_WAITOK); - UNP_LIST_LOCK(); + UNP_LINK_RLOCK(); for (unp = LIST_FIRST(head), i = 0; unp && i < n; unp = LIST_NEXT(unp, unp_link)) { UNP_PCB_LOCK(unp); @@ -1782,7 +1788,7 @@ unp_pcblist(SYSCTL_HANDLER_ARGS) } UNP_PCB_UNLOCK(unp); } - UNP_LIST_UNLOCK(); + UNP_LINK_RUNLOCK(); n = i; /* In case we lost some during malloc. */ error = 0; @@ -2044,7 +2050,6 @@ unp_init(void) TASK_INIT(&unp_defer_task, 0, unp_process_defers, NULL); #endif /* __rtems__ */ UNP_LINK_LOCK_INIT(); - UNP_LIST_LOCK_INIT(); UNP_DEFERRED_LOCK_INIT(); } @@ -2396,8 +2401,7 @@ unp_accessable(struct filedescent **fdep, int fdcount) static void unp_gc_process(struct unpcb *unp) { - struct socket *soa; - struct socket *so; + struct socket *so, *soa; struct file *fp; /* Already processed. */ @@ -2417,28 +2421,30 @@ unp_gc_process(struct unpcb *unp) return; } - /* - * Mark all sockets we reference with RIGHTS. - */ so = unp->unp_socket; - if ((unp->unp_gcflag & UNPGC_IGNORE_RIGHTS) == 0) { - SOCKBUF_LOCK(&so->so_rcv); - unp_scan(so->so_rcv.sb_mb, unp_accessable); - SOCKBUF_UNLOCK(&so->so_rcv); - } - - /* - * Mark all sockets in our accept queue. - */ - ACCEPT_LOCK(); - TAILQ_FOREACH(soa, &so->so_comp, so_list) { - if ((sotounpcb(soa)->unp_gcflag & UNPGC_IGNORE_RIGHTS) != 0) - continue; - SOCKBUF_LOCK(&soa->so_rcv); - unp_scan(soa->so_rcv.sb_mb, unp_accessable); - SOCKBUF_UNLOCK(&soa->so_rcv); + SOCK_LOCK(so); + if (SOLISTENING(so)) { + /* + * Mark all sockets in our accept queue. + */ + TAILQ_FOREACH(soa, &so->sol_comp, so_list) { + if (sotounpcb(soa)->unp_gcflag & UNPGC_IGNORE_RIGHTS) + continue; + SOCKBUF_LOCK(&soa->so_rcv); + unp_scan(soa->so_rcv.sb_mb, unp_accessable); + SOCKBUF_UNLOCK(&soa->so_rcv); + } + } else { + /* + * Mark all sockets we reference with RIGHTS. + */ + if ((unp->unp_gcflag & UNPGC_IGNORE_RIGHTS) == 0) { + SOCKBUF_LOCK(&so->so_rcv); + unp_scan(so->so_rcv.sb_mb, unp_accessable); + SOCKBUF_UNLOCK(&so->so_rcv); + } } - ACCEPT_UNLOCK(); + SOCK_UNLOCK(so); unp->unp_gcflag |= UNPGC_SCANNED; } @@ -2461,7 +2467,7 @@ unp_gc(__unused void *arg, int pending) int i, total; unp_taskcount++; - UNP_LIST_LOCK(); + UNP_LINK_RLOCK(); /* * First clear all gc flags from previous runs, apart from * UNPGC_IGNORE_RIGHTS. @@ -2484,7 +2490,7 @@ unp_gc(__unused void *arg, int pending) LIST_FOREACH(unp, *head, unp_link) unp_gc_process(unp); } while (unp_marked); - UNP_LIST_UNLOCK(); + UNP_LINK_RUNLOCK(); if (unp_unreachable == 0) return; @@ -2499,7 +2505,6 @@ unp_gc(__unused void *arg, int pending) * as as unreachable and store them locally. */ UNP_LINK_RLOCK(); - UNP_LIST_LOCK(); for (total = 0, head = heads; *head != NULL; head++) LIST_FOREACH(unp, *head, unp_link) if ((unp->unp_gcflag & UNPGC_DEAD) != 0) { @@ -2512,7 +2517,6 @@ unp_gc(__unused void *arg, int pending) KASSERT(total <= unp_unreachable, ("unp_gc: incorrect unreachable count.")); } - UNP_LIST_UNLOCK(); UNP_LINK_RUNLOCK(); /* @@ -2555,10 +2559,11 @@ unp_dispose(struct socket *so) struct unpcb *unp; unp = sotounpcb(so); - UNP_LIST_LOCK(); + UNP_LINK_WLOCK(); unp->unp_gcflag |= UNPGC_IGNORE_RIGHTS; - UNP_LIST_UNLOCK(); - unp_dispose_mbuf(so->so_rcv.sb_mb); + UNP_LINK_WUNLOCK(); + if (!SOLISTENING(so)) + unp_dispose_mbuf(so->so_rcv.sb_mb); } static void @@ -2613,7 +2618,6 @@ unp_scan(struct mbuf *m0, void (*op)(struct filedescent **, int)) void vfs_unp_reclaim(struct vnode *vp) { - struct socket *so; struct unpcb *unp; int active; @@ -2623,10 +2627,7 @@ vfs_unp_reclaim(struct vnode *vp) active = 0; UNP_LINK_WLOCK(); - VOP_UNP_CONNECT(vp, &so); - if (so == NULL) - goto done; - unp = sotounpcb(so); + VOP_UNP_CONNECT(vp, &unp); if (unp == NULL) goto done; UNP_PCB_LOCK(unp); @@ -2663,10 +2664,6 @@ db_print_unpflags(int unp_flags) db_printf("%sUNP_HAVEPC", comma ? ", " : ""); comma = 1; } - if (unp_flags & UNP_HAVEPCCACHED) { - db_printf("%sUNP_HAVEPCCACHED", comma ? ", " : ""); - comma = 1; - } if (unp_flags & UNP_WANTCRED) { db_printf("%sUNP_WANTCRED", comma ? ", " : ""); comma = 1; diff --git a/freebsd/sys/net/bpf.c b/freebsd/sys/net/bpf.c index d7a2abf7..b0ecb039 100644 --- a/freebsd/sys/net/bpf.c +++ b/freebsd/sys/net/bpf.c @@ -926,8 +926,6 @@ bpfopen(struct cdev *dev, int flags, int fmt, struct thread *td) return (error); } #else /* __rtems__ */ - u_int size; - d = malloc(sizeof(*d), M_BPF, M_NOWAIT | M_ZERO); if (d == NULL) { return (d); @@ -1360,7 +1358,7 @@ bpfioctl(struct bpf_d *d, u_long cmd, caddr_t addr, int flags, #endif case BIOCGETIF: case BIOCGRTIMEOUT: -#if defined(COMPAT_FREEBSD32) && !defined(__mips__) +#if defined(COMPAT_FREEBSD32) && defined(__amd64__) case BIOCGRTIMEOUT32: #endif case BIOCGSTATS: @@ -1372,7 +1370,7 @@ bpfioctl(struct bpf_d *d, u_long cmd, caddr_t addr, int flags, case FIONREAD: case BIOCLOCK: case BIOCSRTIMEOUT: -#if defined(COMPAT_FREEBSD32) && !defined(__mips__) +#if defined(COMPAT_FREEBSD32) && defined(__amd64__) case BIOCSRTIMEOUT32: #endif case BIOCIMMEDIATE: @@ -1596,7 +1594,7 @@ bpfioctl(struct bpf_d *d, u_long cmd, caddr_t addr, int flags, * Set read timeout. */ case BIOCSRTIMEOUT: -#if defined(COMPAT_FREEBSD32) && !defined(__mips__) +#if defined(COMPAT_FREEBSD32) && defined(__amd64__) case BIOCSRTIMEOUT32: #endif { @@ -1627,12 +1625,12 @@ bpfioctl(struct bpf_d *d, u_long cmd, caddr_t addr, int flags, * Get read timeout. */ case BIOCGRTIMEOUT: -#if defined(COMPAT_FREEBSD32) && !defined(__mips__) +#if defined(COMPAT_FREEBSD32) && defined(__amd64__) case BIOCGRTIMEOUT32: #endif { struct timeval *tv; -#if defined(COMPAT_FREEBSD32) && !defined(__mips__) +#if defined(COMPAT_FREEBSD32) && defined(__amd64__) struct timeval32 *tv32; struct timeval tv64; @@ -1644,7 +1642,7 @@ bpfioctl(struct bpf_d *d, u_long cmd, caddr_t addr, int flags, tv->tv_sec = d->bd_rtout / hz; tv->tv_usec = (d->bd_rtout % hz) * tick; -#if defined(COMPAT_FREEBSD32) && !defined(__mips__) +#if defined(COMPAT_FREEBSD32) && defined(__amd64__) if (cmd == BIOCGRTIMEOUT32) { tv32 = (struct timeval32 *)addr; tv32->tv_sec = tv->tv_sec; diff --git a/freebsd/sys/net/ethernet.h b/freebsd/sys/net/ethernet.h index bc5fa9cb..5cd1dc50 100644 --- a/freebsd/sys/net/ethernet.h +++ b/freebsd/sys/net/ethernet.h @@ -406,6 +406,12 @@ void ether_vlan_mtap(struct bpf_if *, struct mbuf *, void *, u_int); struct mbuf *ether_vlanencap(struct mbuf *, uint16_t); +#ifdef _SYS_EVENTHANDLER_H_ +/* new ethernet interface attached event */ +typedef void (*ether_ifattach_event_handler_t)(void *, struct ifnet *); +EVENTHANDLER_DECLARE(ether_ifattach_event, ether_ifattach_event_handler_t); +#endif + #else /* _KERNEL */ #include <sys/cdefs.h> diff --git a/freebsd/sys/net/if_enc.c b/freebsd/sys/net/if_enc.c index 42b399d2..2a0b17d3 100644 --- a/freebsd/sys/net/if_enc.c +++ b/freebsd/sys/net/if_enc.c @@ -286,7 +286,7 @@ enc_hhook(int32_t hhook_type, int32_t hhook_id, void *udata, void *ctx_data, /* Make a packet looks like it was received on enc(4) */ rcvif = (*ctx->mp)->m_pkthdr.rcvif; (*ctx->mp)->m_pkthdr.rcvif = ifp; - if (pfil_run_hooks(ph, ctx->mp, ifp, pdir, NULL) != 0 || + if (pfil_run_hooks(ph, ctx->mp, ifp, pdir, ctx->inp) != 0 || *ctx->mp == NULL) { *ctx->mp = NULL; /* consumed by filter */ return (EACCES); diff --git a/freebsd/sys/net/if_enc.h b/freebsd/sys/net/if_enc.h index 941ed12a..616c621f 100644 --- a/freebsd/sys/net/if_enc.h +++ b/freebsd/sys/net/if_enc.h @@ -33,6 +33,7 @@ struct ipsec_ctx_data { struct mbuf **mp; struct secasvar *sav; + struct inpcb *inp; uint8_t af; #define IPSEC_ENC_BEFORE 0x01 #define IPSEC_ENC_AFTER 0x02 diff --git a/freebsd/sys/net/if_ethersubr.c b/freebsd/sys/net/if_ethersubr.c index c3c89d24..c0064fc6 100644 --- a/freebsd/sys/net/if_ethersubr.c +++ b/freebsd/sys/net/if_ethersubr.c @@ -40,6 +40,8 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/bus.h> +#include <sys/eventhandler.h> #include <sys/kernel.h> #include <sys/lock.h> #include <sys/malloc.h> @@ -933,6 +935,11 @@ ether_ifattach(struct ifnet *ifp, const u_int8_t *lla) if_printf(ifp, "Ethernet address: %6D\n", lla, ":"); uuid_ether_add(LLADDR(sdl)); + + /* Add necessary bits are setup; announce it now. */ + EVENTHANDLER_INVOKE(ether_ifattach_event, ifp); + if (IS_DEFAULT_VNET(curvnet)) + devctl_notify("ETHERNET", ifp->if_xname, "IFATTACH", NULL); } /* diff --git a/freebsd/sys/net/if_lagg.c b/freebsd/sys/net/if_lagg.c index 4d6e919e..ce696fb2 100644 --- a/freebsd/sys/net/if_lagg.c +++ b/freebsd/sys/net/if_lagg.c @@ -740,15 +740,16 @@ lagg_port_create(struct lagg_softc *sc, struct ifnet *ifp) lagg_setmulti(lp); + LAGG_WUNLOCK(sc); + if ((error = lagg_proto_addport(sc, lp)) != 0) { /* Remove the port, without calling pr_delport. */ + LAGG_WLOCK(sc); lagg_port_destroy(lp, 0); LAGG_UNLOCK_ASSERT(sc); return (error); } - LAGG_WUNLOCK(sc); - /* Update lagg capabilities */ lagg_capabilities(sc); lagg_linkstate(sc); diff --git a/freebsd/sys/net/if_stf.c b/freebsd/sys/net/if_stf.c index 719dd1fa..b29c5f93 100644 --- a/freebsd/sys/net/if_stf.c +++ b/freebsd/sys/net/if_stf.c @@ -141,7 +141,6 @@ SYSCTL_INT(_net_link_stf, OID_AUTO, permit_rfc1918, CTLFLAG_RWTUN, struct stf_softc { struct ifnet *sc_ifp; - struct mtx sc_ro_mtx; u_int sc_fibnum; const struct encaptab *encap_cookie; }; @@ -149,10 +148,6 @@ struct stf_softc { static const char stfname[] = "stf"; -/* - * Note that mutable fields in the softc are not currently locked. - * We do lock sc_ro in stf_output though. - */ static MALLOC_DEFINE(M_STF, stfname, "6to4 Tunnel Interface"); static const int ip_stf_ttl = 40; @@ -259,7 +254,6 @@ stf_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) ifp->if_dname = stfname; ifp->if_dunit = IF_DUNIT_NONE; - mtx_init(&(sc)->sc_ro_mtx, "stf ro", NULL, MTX_DEF); sc->encap_cookie = encap_attach_func(AF_INET, IPPROTO_IPV6, stf_encapcheck, &in_stf_protosw, sc); if (sc->encap_cookie == NULL) { @@ -286,7 +280,6 @@ stf_clone_destroy(struct if_clone *ifc, struct ifnet *ifp) err = encap_detach(sc->encap_cookie); KASSERT(err == 0, ("Unexpected error detaching encap_cookie")); - mtx_destroy(&(sc)->sc_ro_mtx); bpfdetach(ifp); if_detach(ifp); if_free(ifp); diff --git a/freebsd/sys/net/iflib.h b/freebsd/sys/net/iflib.h index 8bd20bfc..6ac75dbb 100644 --- a/freebsd/sys/net/iflib.h +++ b/freebsd/sys/net/iflib.h @@ -33,7 +33,6 @@ #include <sys/bus.h> #include <sys/cpuset.h> #include <machine/bus.h> -#include <sys/bus_dma.h> #include <sys/nv.h> #include <sys/gtaskqueue.h> @@ -201,8 +200,6 @@ typedef struct if_softc_ctx { uint8_t isc_txd_size[8]; uint8_t isc_rxd_size[8]; - int isc_max_txqsets; - int isc_max_rxqsets; int isc_tx_tso_segments_max; int isc_tx_tso_size_max; int isc_tx_tso_segsize_max; diff --git a/freebsd/sys/net/route.c b/freebsd/sys/net/route.c index f7768737..a6a19cb0 100644 --- a/freebsd/sys/net/route.c +++ b/freebsd/sys/net/route.c @@ -61,7 +61,6 @@ #include <net/route.h> #include <net/route_var.h> #include <net/vnet.h> -#include <net/flowtable.h> #ifdef RADIX_MPATH #include <net/radix_mpath.h> @@ -486,18 +485,23 @@ rtalloc1_fib(struct sockaddr *dst, int report, u_long ignflags, /* * Look up the address in the table for that Address Family */ - RIB_RLOCK(rh); + if ((ignflags & RTF_RNH_LOCKED) == 0) + RIB_RLOCK(rh); +#ifdef INVARIANTS + else + RIB_LOCK_ASSERT(rh); +#endif rn = rh->rnh_matchaddr(dst, &rh->head); if (rn && ((rn->rn_flags & RNF_ROOT) == 0)) { newrt = RNTORT(rn); RT_LOCK(newrt); RT_ADDREF(newrt); - RIB_RUNLOCK(rh); + if ((ignflags & RTF_RNH_LOCKED) == 0) + RIB_RUNLOCK(rh); return (newrt); - } else + } else if ((ignflags & RTF_RNH_LOCKED) == 0) RIB_RUNLOCK(rh); - /* * Either we hit the root or could not find any match, * which basically means: "cannot get there from here". @@ -780,7 +784,9 @@ ifa_ifwithroute(int flags, const struct sockaddr *dst, struct sockaddr *gateway, if (ifa == NULL) ifa = ifa_ifwithnet(gateway, 0, fibnum); if (ifa == NULL) { - struct rtentry *rt = rtalloc1_fib(gateway, 0, 0, fibnum); + struct rtentry *rt; + + rt = rtalloc1_fib(gateway, 0, flags, fibnum); if (rt == NULL) return (NULL); /* @@ -1529,79 +1535,12 @@ rt_mpath_unlink(struct rib_head *rnh, struct rt_addrinfo *info, } #endif -#ifdef FLOWTABLE -static struct rtentry * -rt_flowtable_check_route(struct rib_head *rnh, struct rt_addrinfo *info) -{ -#if defined(INET6) || defined(INET) - struct radix_node *rn; -#endif - struct rtentry *rt0; - - rt0 = NULL; - /* "flow-table" only supports IPv6 and IPv4 at the moment. */ - switch (dst->sa_family) { -#ifdef INET6 - case AF_INET6: -#endif -#ifdef INET - case AF_INET: -#endif -#if defined(INET6) || defined(INET) - rn = rnh->rnh_matchaddr(dst, &rnh->head); - if (rn && ((rn->rn_flags & RNF_ROOT) == 0)) { - struct sockaddr *mask; - u_char *m, *n; - int len; - - /* - * compare mask to see if the new route is - * more specific than the existing one - */ - rt0 = RNTORT(rn); - RT_LOCK(rt0); - RT_ADDREF(rt0); - RT_UNLOCK(rt0); - /* - * A host route is already present, so - * leave the flow-table entries as is. - */ - if (rt0->rt_flags & RTF_HOST) { - RTFREE(rt0); - rt0 = NULL; - } else if (!(flags & RTF_HOST) && netmask) { - mask = rt_mask(rt0); - len = mask->sa_len; - m = (u_char *)mask; - n = (u_char *)netmask; - while (len-- > 0) { - if (*n != *m) - break; - n++; - m++; - } - if (len == 0 || (*n < *m)) { - RTFREE(rt0); - rt0 = NULL; - } - } - } -#endif/* INET6 || INET */ - } - - return (rt0); -} -#endif - int rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, u_int fibnum) { int error = 0; struct rtentry *rt, *rt_old; -#ifdef FLOWTABLE - struct rtentry *rt0; -#endif struct radix_node *rn; struct rib_head *rnh; struct ifaddr *ifa; @@ -1735,10 +1674,6 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, } #endif -#ifdef FLOWTABLE - rt0 = rt_flowtable_check_route(rnh, info); -#endif /* FLOWTABLE */ - /* XXX mtu manipulation will be done in rnh_addaddr -- itojun */ rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head, rt->rt_nodes); @@ -1773,18 +1708,8 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, ifa_free(rt->rt_ifa); R_Free(rt_key(rt)); uma_zfree(V_rtzone, rt); -#ifdef FLOWTABLE - if (rt0 != NULL) - RTFREE(rt0); -#endif return (EEXIST); } -#ifdef FLOWTABLE - else if (rt0 != NULL) { - flowtable_route_flush(dst->sa_family, rt0); - RTFREE(rt0); - } -#endif if (rt_old != NULL) { rt_notifydelete(rt_old, info); @@ -1870,8 +1795,13 @@ rtrequest1_fib_change(struct rib_head *rnh, struct rt_addrinfo *info, info->rti_info[RTAX_IFP] != NULL || (info->rti_info[RTAX_IFA] != NULL && !sa_equal(info->rti_info[RTAX_IFA], rt->rt_ifa->ifa_addr))) { - + /* + * XXX: Temporarily set RTF_RNH_LOCKED flag in the rti_flags + * to avoid rlock in the ifa_ifwithroute(). + */ + info->rti_flags |= RTF_RNH_LOCKED; error = rt_getifa_fib(info, fibnum); + info->rti_flags &= ~RTF_RNH_LOCKED; if (info->rti_ifa != NULL) free_ifa = 1; diff --git a/freebsd/sys/net/route.h b/freebsd/sys/net/route.h index d4bc4056..444559b5 100644 --- a/freebsd/sys/net/route.h +++ b/freebsd/sys/net/route.h @@ -189,7 +189,7 @@ struct rtentry { /* 0x8000000 and up unassigned */ #define RTF_STICKY 0x10000000 /* always route dst->src */ -#define RTF_RNH_LOCKED 0x40000000 /* unused */ +#define RTF_RNH_LOCKED 0x40000000 /* radix node head is locked */ #define RTF_GWFLAG_COMPAT 0x80000000 /* a compatibility bit for interacting with existing routing apps */ diff --git a/freebsd/sys/net80211/ieee80211_amrr.c b/freebsd/sys/net80211/ieee80211_amrr.c index 84cc0f9b..42e9ac1a 100644 --- a/freebsd/sys/net80211/ieee80211_amrr.c +++ b/freebsd/sys/net80211/ieee80211_amrr.c @@ -119,6 +119,7 @@ amrr_init(struct ieee80211vap *vap) KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__)); + nrefs++; /* XXX locking */ amrr = vap->iv_rs = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr), M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); if (amrr == NULL) { @@ -135,6 +136,8 @@ static void amrr_deinit(struct ieee80211vap *vap) { IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL); + KASSERT(nrefs > 0, ("imbalanced attach/detach")); + nrefs--; /* XXX locking */ } /* diff --git a/freebsd/sys/net80211/ieee80211_output.c b/freebsd/sys/net80211/ieee80211_output.c index 5e67b82d..85bfeb0a 100644 --- a/freebsd/sys/net80211/ieee80211_output.c +++ b/freebsd/sys/net80211/ieee80211_output.c @@ -781,6 +781,9 @@ ieee80211_send_setup( tap = &ni->ni_tx_ampdu[tid]; if (tid != IEEE80211_NONQOS_TID && IEEE80211_AMPDU_RUNNING(tap)) { m->m_flags |= M_AMPDU_MPDU; + + /* NB: zero out i_seq field (for s/w encryption etc) */ + *(uint16_t *)&wh->i_seq[0] = 0; } else { if (IEEE80211_HAS_SEQ(type & IEEE80211_FC0_TYPE_MASK, type & IEEE80211_FC0_SUBTYPE_MASK)) @@ -1612,6 +1615,9 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, *(uint16_t *)wh->i_seq = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); M_SEQNO_SET(m, seqno); + } else { + /* NB: zero out i_seq field (for s/w encryption etc) */ + *(uint16_t *)wh->i_seq = 0; } } else { /* diff --git a/freebsd/sys/net80211/ieee80211_rssadapt.c b/freebsd/sys/net80211/ieee80211_rssadapt.c index e4421e6a..5d404fa1 100644 --- a/freebsd/sys/net80211/ieee80211_rssadapt.c +++ b/freebsd/sys/net80211/ieee80211_rssadapt.c @@ -132,7 +132,8 @@ rssadapt_init(struct ieee80211vap *vap) KASSERT(vap->iv_rs == NULL, ("%s: iv_rs already initialized", __func__)); - + + nrefs++; /* XXX locking */ vap->iv_rs = rs = IEEE80211_MALLOC(sizeof(struct ieee80211_rssadapt), M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); if (rs == NULL) { @@ -148,6 +149,8 @@ static void rssadapt_deinit(struct ieee80211vap *vap) { IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL); + KASSERT(nrefs > 0, ("imbalanced attach/detach")); + nrefs--; /* XXX locking */ } static void diff --git a/freebsd/sys/netinet/cc/cc_newreno.c b/freebsd/sys/netinet/cc/cc_newreno.c index fb6c1087..474afcc9 100644 --- a/freebsd/sys/netinet/cc/cc_newreno.c +++ b/freebsd/sys/netinet/cc/cc_newreno.c @@ -184,42 +184,30 @@ newreno_after_idle(struct cc_var *ccv) static void newreno_cong_signal(struct cc_var *ccv, uint32_t type) { - uint32_t cwin, ssthresh_on_loss; - u_int mss; - - cwin = CCV(ccv, snd_cwnd); - mss = CCV(ccv, t_maxseg); - ssthresh_on_loss = - max((CCV(ccv, snd_max) - CCV(ccv, snd_una)) / 2 / mss, 2) - * mss; + u_int win; /* Catch algos which mistakenly leak private signal types. */ KASSERT((type & CC_SIGPRIVMASK) == 0, ("%s: congestion signal type 0x%08x is private\n", __func__, type)); - cwin = max(cwin / 2 / mss, 2) * mss; + win = max(CCV(ccv, snd_cwnd) / 2 / CCV(ccv, t_maxseg), 2) * + CCV(ccv, t_maxseg); switch (type) { case CC_NDUPACK: if (!IN_FASTRECOVERY(CCV(ccv, t_flags))) { - if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) { - CCV(ccv, snd_ssthresh) = ssthresh_on_loss; - CCV(ccv, snd_cwnd) = cwin; - } + if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) + CCV(ccv, snd_ssthresh) = win; ENTER_RECOVERY(CCV(ccv, t_flags)); } break; case CC_ECN: if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) { - CCV(ccv, snd_ssthresh) = ssthresh_on_loss; - CCV(ccv, snd_cwnd) = cwin; + CCV(ccv, snd_ssthresh) = win; + CCV(ccv, snd_cwnd) = win; ENTER_CONGRECOVERY(CCV(ccv, t_flags)); } break; - case CC_RTO: - CCV(ccv, snd_ssthresh) = ssthresh_on_loss; - CCV(ccv, snd_cwnd) = mss; - break; } } diff --git a/freebsd/sys/netinet/ip_output.c b/freebsd/sys/netinet/ip_output.c index 234f19fd..d9a5c511 100644 --- a/freebsd/sys/netinet/ip_output.c +++ b/freebsd/sys/netinet/ip_output.c @@ -65,7 +65,6 @@ __FBSDID("$FreeBSD$"); #include <net/netisr.h> #include <net/pfil.h> #include <net/route.h> -#include <net/flowtable.h> #ifdef RADIX_MPATH #include <net/radix_mpath.h> #endif @@ -246,11 +245,6 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, int flags, bzero(ro, sizeof (*ro)); } -#ifdef FLOWTABLE - if (ro->ro_rt == NULL) - (void )flowtable_lookup(AF_INET, m, ro); -#endif - if (opt) { int len = 0; m = ip_insertoptions(m, opt, &len); diff --git a/freebsd/sys/netinet/sctp_asconf.c b/freebsd/sys/netinet/sctp_asconf.c index 04a813d3..aa31e488 100644 --- a/freebsd/sys/netinet/sctp_asconf.c +++ b/freebsd/sys/netinet/sctp_asconf.c @@ -184,7 +184,7 @@ sctp_process_asconf_add_ip(struct sockaddr *src, struct sctp_asconf_paramhdr *ap } v4addr = (struct sctp_ipv4addr_param *)ph; sin = &store.sin; - bzero(sin, sizeof(*sin)); + memset(sin, 0, sizeof(*sin)); sin->sin_family = AF_INET; sin->sin_len = sizeof(struct sockaddr_in); sin->sin_port = stcb->rport; @@ -207,7 +207,7 @@ sctp_process_asconf_add_ip(struct sockaddr *src, struct sctp_asconf_paramhdr *ap } v6addr = (struct sctp_ipv6addr_param *)ph; sin6 = &store.sin6; - bzero(sin6, sizeof(*sin6)); + memset(sin6, 0, sizeof(*sin6)); sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_port = stcb->rport; @@ -334,7 +334,7 @@ sctp_process_asconf_delete_ip(struct sockaddr *src, } v4addr = (struct sctp_ipv4addr_param *)ph; sin = &store.sin; - bzero(sin, sizeof(*sin)); + memset(sin, 0, sizeof(*sin)); sin->sin_family = AF_INET; sin->sin_len = sizeof(struct sockaddr_in); sin->sin_port = stcb->rport; @@ -354,7 +354,7 @@ sctp_process_asconf_delete_ip(struct sockaddr *src, } v6addr = (struct sctp_ipv6addr_param *)ph; sin6 = &store.sin6; - bzero(sin6, sizeof(*sin6)); + memset(sin6, 0, sizeof(*sin6)); sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_port = stcb->rport; @@ -463,7 +463,7 @@ sctp_process_asconf_set_primary(struct sockaddr *src, } v4addr = (struct sctp_ipv4addr_param *)ph; sin = &store.sin; - bzero(sin, sizeof(*sin)); + memset(sin, 0, sizeof(*sin)); sin->sin_family = AF_INET; sin->sin_len = sizeof(struct sockaddr_in); sin->sin_addr.s_addr = v4addr->addr; @@ -481,7 +481,7 @@ sctp_process_asconf_set_primary(struct sockaddr *src, } v6addr = (struct sctp_ipv6addr_param *)ph; sin6 = &store.sin6; - bzero(sin6, sizeof(*sin6)); + memset(sin6, 0, sizeof(*sin6)); sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(struct sockaddr_in6); memcpy((caddr_t)&sin6->sin6_addr, v6addr->addr, @@ -2606,7 +2606,7 @@ sctp_compose_asconf(struct sctp_tcb *stcb, int *retlen, int addr_locked) SCTP_BUF_LEN(m_asconf_chk) = sizeof(struct sctp_asconf_chunk); SCTP_BUF_LEN(m_asconf) = 0; acp = mtod(m_asconf_chk, struct sctp_asconf_chunk *); - bzero(acp, sizeof(struct sctp_asconf_chunk)); + memset(acp, 0, sizeof(struct sctp_asconf_chunk)); /* save pointers to lookup address and asconf params */ lookup_ptr = (caddr_t)(acp + 1); /* after the header */ ptr = mtod(m_asconf, caddr_t); /* beginning of cluster */ @@ -2739,7 +2739,7 @@ sctp_compose_asconf(struct sctp_tcb *stcb, int *retlen, int addr_locked) /* XXX for now, we send a IPv4 address of 0.0.0.0 */ lookup->ph.param_type = htons(SCTP_IPV4_ADDRESS); lookup->ph.param_length = htons(SCTP_SIZE32(sizeof(struct sctp_ipv4addr_param))); - bzero(lookup->addr, sizeof(struct in_addr)); + memset(lookup->addr, 0, sizeof(struct in_addr)); SCTP_BUF_LEN(m_asconf_chk) += SCTP_SIZE32(sizeof(struct sctp_ipv4addr_param)); } } diff --git a/freebsd/sys/netinet/sctp_auth.c b/freebsd/sys/netinet/sctp_auth.c index 4bcb2459..b6dfdaf8 100644 --- a/freebsd/sys/netinet/sctp_auth.c +++ b/freebsd/sys/netinet/sctp_auth.c @@ -55,7 +55,7 @@ __FBSDID("$FreeBSD$"); void sctp_clear_chunklist(sctp_auth_chklist_t *chklist) { - bzero(chklist, sizeof(*chklist)); + memset(chklist, 0, sizeof(*chklist)); /* chklist->num_chunks = 0; */ } @@ -94,7 +94,7 @@ sctp_copy_chunklist(sctp_auth_chklist_t *list) if (new_list == NULL) return (NULL); /* copy it */ - bcopy(list, new_list, sizeof(*new_list)); + memcpy(new_list, list, sizeof(*new_list)); return (new_list); } @@ -340,7 +340,7 @@ sctp_set_key(uint8_t *key, uint32_t keylen) /* out of memory */ return (NULL); } - bcopy(key, new_key->key, keylen); + memcpy(new_key->key, key, keylen); return (new_key); } @@ -429,28 +429,28 @@ sctp_compute_hashkey(sctp_key_t *key1, sctp_key_t *key2, sctp_key_t *shared) if (sctp_compare_key(key1, key2) <= 0) { /* key is shared + key1 + key2 */ if (sctp_get_keylen(shared)) { - bcopy(shared->key, key_ptr, shared->keylen); + memcpy(key_ptr, shared->key, shared->keylen); key_ptr += shared->keylen; } if (sctp_get_keylen(key1)) { - bcopy(key1->key, key_ptr, key1->keylen); + memcpy(key_ptr, key1->key, key1->keylen); key_ptr += key1->keylen; } if (sctp_get_keylen(key2)) { - bcopy(key2->key, key_ptr, key2->keylen); + memcpy(key_ptr, key2->key, key2->keylen); } } else { /* key is shared + key2 + key1 */ if (sctp_get_keylen(shared)) { - bcopy(shared->key, key_ptr, shared->keylen); + memcpy(key_ptr, shared->key, shared->keylen); key_ptr += shared->keylen; } if (sctp_get_keylen(key2)) { - bcopy(key2->key, key_ptr, key2->keylen); + memcpy(key_ptr, key2->key, key2->keylen); key_ptr += key2->keylen; } if (sctp_get_keylen(key1)) { - bcopy(key1->key, key_ptr, key1->keylen); + memcpy(key_ptr, key1->key, key1->keylen); } } return (new_key); @@ -766,7 +766,7 @@ sctp_serialize_hmaclist(sctp_hmaclist_t *list, uint8_t *ptr) for (i = 0; i < list->num_algo; i++) { hmac_id = htons(list->hmac[i]); - bcopy(&hmac_id, ptr, sizeof(hmac_id)); + memcpy(ptr, &hmac_id, sizeof(hmac_id)); ptr += sizeof(hmac_id); } return (list->num_algo * sizeof(hmac_id)); @@ -797,7 +797,7 @@ sctp_alloc_authinfo(void) /* out of memory */ return (NULL); } - bzero(new_authinfo, sizeof(*new_authinfo)); + memset(new_authinfo, 0, sizeof(*new_authinfo)); return (new_authinfo); } @@ -955,10 +955,10 @@ sctp_hmac(uint16_t hmac_algo, uint8_t *key, uint32_t keylen, key = temp; } /* initialize the inner/outer pads with the key and "append" zeroes */ - bzero(ipad, blocklen); - bzero(opad, blocklen); - bcopy(key, ipad, keylen); - bcopy(key, opad, keylen); + memset(ipad, 0, blocklen); + memset(opad, 0, blocklen); + memcpy(ipad, key, keylen); + memcpy(opad, key, keylen); /* XOR the key with ipad and opad values */ for (i = 0; i < blocklen; i++) { @@ -1015,10 +1015,10 @@ sctp_hmac_m(uint16_t hmac_algo, uint8_t *key, uint32_t keylen, key = temp; } /* initialize the inner/outer pads with the key and "append" zeroes */ - bzero(ipad, blocklen); - bzero(opad, blocklen); - bcopy(key, ipad, keylen); - bcopy(key, opad, keylen); + memset(ipad, 0, blocklen); + memset(opad, 0, blocklen); + memcpy(ipad, key, keylen); + memcpy(opad, key, keylen); /* XOR the key with ipad and opad values */ for (i = 0; i < blocklen; i++) { @@ -1126,7 +1126,7 @@ sctp_compute_hmac(uint16_t hmac_algo, sctp_key_t *key, uint8_t *text, sctp_hmac_final(hmac_algo, &ctx, temp); /* save the hashed key as the new key */ key->keylen = digestlen; - bcopy(temp, key->key, key->keylen); + memcpy(key->key, temp, key->keylen); } return (sctp_hmac(hmac_algo, key->key, key->keylen, text, textlen, digest)); @@ -1160,7 +1160,7 @@ sctp_compute_hmac_m(uint16_t hmac_algo, sctp_key_t *key, struct mbuf *m, sctp_hmac_final(hmac_algo, &ctx, temp); /* save the hashed key as the new key */ key->keylen = digestlen; - bcopy(temp, key->key, key->keylen); + memcpy(key->key, temp, key->keylen); } return (sctp_hmac_m(hmac_algo, key->key, key->keylen, m, m_offset, digest, 0)); } @@ -1436,7 +1436,7 @@ sctp_auth_get_cookie_params(struct sctp_tcb *stcb, struct mbuf *m, if (plen > sizeof(random_store)) break; phdr = sctp_get_next_param(m, offset, - (struct sctp_paramhdr *)random_store, min(plen, sizeof(random_store))); + (struct sctp_paramhdr *)random_store, plen); if (phdr == NULL) return; /* save the random and length for the key */ @@ -1449,7 +1449,7 @@ sctp_auth_get_cookie_params(struct sctp_tcb *stcb, struct mbuf *m, if (plen > sizeof(hmacs_store)) break; phdr = sctp_get_next_param(m, offset, - (struct sctp_paramhdr *)hmacs_store, min(plen, sizeof(hmacs_store))); + (struct sctp_paramhdr *)hmacs_store, plen); if (phdr == NULL) return; /* save the hmacs list and num for the key */ @@ -1471,7 +1471,7 @@ sctp_auth_get_cookie_params(struct sctp_tcb *stcb, struct mbuf *m, if (plen > sizeof(chunks_store)) break; phdr = sctp_get_next_param(m, offset, - (struct sctp_paramhdr *)chunks_store, min(plen, sizeof(chunks_store))); + (struct sctp_paramhdr *)chunks_store, plen); if (phdr == NULL) return; chunks = (struct sctp_auth_chunk_list *)phdr; @@ -1503,17 +1503,17 @@ sctp_auth_get_cookie_params(struct sctp_tcb *stcb, struct mbuf *m, /* copy in the RANDOM */ if (p_random != NULL) { keylen = sizeof(*p_random) + random_len; - bcopy(p_random, new_key->key, keylen); + memcpy(new_key->key, p_random, keylen); } /* append in the AUTH chunks */ if (chunks != NULL) { - bcopy(chunks, new_key->key + keylen, + memcpy(new_key->key + keylen, chunks, sizeof(*chunks) + num_chunks); keylen += sizeof(*chunks) + num_chunks; } /* append in the HMACs */ if (hmacs != NULL) { - bcopy(hmacs, new_key->key + keylen, + memcpy(new_key->key + keylen, hmacs, sizeof(*hmacs) + hmacs_len); } } @@ -1552,7 +1552,7 @@ sctp_fill_hmac_digest_m(struct mbuf *m, uint32_t auth_offset, /* zero the digest + chunk padding */ digestlen = sctp_get_hmac_digest_len(stcb->asoc.peer_hmac_id); - bzero(auth->hmac, SCTP_SIZE32(digestlen)); + memset(auth->hmac, 0, SCTP_SIZE32(digestlen)); /* is the desired key cached? */ if ((keyid != stcb->asoc.authinfo.assoc_keyid) || @@ -1590,7 +1590,7 @@ sctp_fill_hmac_digest_m(struct mbuf *m, uint32_t auth_offset, static void -sctp_bzero_m(struct mbuf *m, uint32_t m_offset, uint32_t size) +sctp_zero_m(struct mbuf *m, uint32_t m_offset, uint32_t size) { struct mbuf *m_tmp; uint8_t *data; @@ -1609,10 +1609,10 @@ sctp_bzero_m(struct mbuf *m, uint32_t m_offset, uint32_t size) while ((m_tmp != NULL) && (size > 0)) { data = mtod(m_tmp, uint8_t *)+m_offset; if (size > (uint32_t)SCTP_BUF_LEN(m_tmp)) { - bzero(data, SCTP_BUF_LEN(m_tmp)); + memset(data, 0, SCTP_BUF_LEN(m_tmp)); size -= SCTP_BUF_LEN(m_tmp); } else { - bzero(data, size); + memset(data, 0, size); size = 0; } /* clear the offset since it's only for the first mbuf */ @@ -1729,8 +1729,8 @@ sctp_handle_auth(struct sctp_tcb *stcb, struct sctp_auth_chunk *auth, return (-1); } /* save a copy of the digest, zero the pseudo header, and validate */ - bcopy(auth->hmac, digest, digestlen); - sctp_bzero_m(m, offset + sizeof(*auth), SCTP_SIZE32(digestlen)); + memcpy(digest, auth->hmac, digestlen); + sctp_zero_m(m, offset + sizeof(*auth), SCTP_SIZE32(digestlen)); (void)sctp_compute_hmac_m(hmac_id, stcb->asoc.authinfo.recv_key, m, offset, computed_digest); @@ -1799,8 +1799,8 @@ sctp_notify_authentication(struct sctp_tcb *stcb, uint32_t indication, sctp_m_freem(m_notify); return; } - control->spec_flags = M_NOTIFICATION; control->length = SCTP_BUF_LEN(m_notify); + control->spec_flags = M_NOTIFICATION; /* not that we need this */ control->tail_mbuf = m_notify; sctp_add_to_readq(stcb->sctp_ep, stcb, control, @@ -1816,7 +1816,7 @@ sctp_notify_authentication(struct sctp_tcb *stcb, uint32_t indication, int sctp_validate_init_auth_params(struct mbuf *m, int offset, int limit) { - struct sctp_paramhdr *phdr, parm_buf; + struct sctp_paramhdr *phdr, param_buf; uint16_t ptype, plen; int peer_supports_asconf = 0; int peer_supports_auth = 0; @@ -1825,7 +1825,7 @@ sctp_validate_init_auth_params(struct mbuf *m, int offset, int limit) uint8_t saw_asconf_ack = 0; /* go through each of the params. */ - phdr = sctp_get_next_param(m, offset, &parm_buf, sizeof(parm_buf)); + phdr = sctp_get_next_param(m, offset, ¶m_buf, sizeof(param_buf)); while (phdr) { ptype = ntohs(phdr->param_type); plen = ntohs(phdr->param_length); @@ -1839,11 +1839,15 @@ sctp_validate_init_auth_params(struct mbuf *m, int offset, int limit) if (ptype == SCTP_SUPPORTED_CHUNK_EXT) { /* A supported extension chunk */ struct sctp_supported_chunk_types_param *pr_supported; - uint8_t local_store[SCTP_PARAM_BUFFER_SIZE]; + uint8_t local_store[SCTP_SMALL_CHUNK_STORE]; int num_ent, i; + if (plen > sizeof(local_store)) { + break; + } phdr = sctp_get_next_param(m, offset, - (struct sctp_paramhdr *)&local_store, min(plen, sizeof(local_store))); + (struct sctp_paramhdr *)&local_store, + plen); if (phdr == NULL) { return (-1); } @@ -1861,7 +1865,6 @@ sctp_validate_init_auth_params(struct mbuf *m, int offset, int limit) } } } else if (ptype == SCTP_RANDOM) { - got_random = 1; /* enforce the random length */ if (plen != (sizeof(struct sctp_auth_random) + SCTP_AUTH_RANDOM_SIZE_REQUIRED)) { @@ -1869,20 +1872,23 @@ sctp_validate_init_auth_params(struct mbuf *m, int offset, int limit) "SCTP: invalid RANDOM len\n"); return (-1); } + got_random = 1; } else if (ptype == SCTP_HMAC_LIST) { - uint8_t store[SCTP_PARAM_BUFFER_SIZE]; struct sctp_auth_hmac_algo *hmacs; + uint8_t store[SCTP_PARAM_BUFFER_SIZE]; int num_hmacs; - if (plen > sizeof(store)) + if (plen > sizeof(store)) { break; + } phdr = sctp_get_next_param(m, offset, - (struct sctp_paramhdr *)store, min(plen, sizeof(store))); - if (phdr == NULL) + (struct sctp_paramhdr *)store, + plen); + if (phdr == NULL) { return (-1); + } hmacs = (struct sctp_auth_hmac_algo *)phdr; - num_hmacs = (plen - sizeof(*hmacs)) / - sizeof(hmacs->hmac_ids[0]); + num_hmacs = (plen - sizeof(*hmacs)) / sizeof(hmacs->hmac_ids[0]); /* validate the hmac list */ if (sctp_verify_hmac_param(hmacs, num_hmacs)) { SCTPDBG(SCTP_DEBUG_AUTH1, @@ -1891,18 +1897,19 @@ sctp_validate_init_auth_params(struct mbuf *m, int offset, int limit) } got_hmacs = 1; } else if (ptype == SCTP_CHUNK_LIST) { - int i, num_chunks; + struct sctp_auth_chunk_list *chunks; uint8_t chunks_store[SCTP_SMALL_CHUNK_STORE]; + int i, num_chunks; - /* did the peer send a non-empty chunk list? */ - struct sctp_auth_chunk_list *chunks = NULL; - + if (plen > sizeof(chunks_store)) { + break; + } phdr = sctp_get_next_param(m, offset, (struct sctp_paramhdr *)chunks_store, - min(plen, sizeof(chunks_store))); - if (phdr == NULL) + plen); + if (phdr == NULL) { return (-1); - + } /*- * Flip through the list and mark that the * peer supports asconf/asconf_ack. @@ -1924,8 +1931,8 @@ sctp_validate_init_auth_params(struct mbuf *m, int offset, int limit) if (offset >= limit) { break; } - phdr = sctp_get_next_param(m, offset, &parm_buf, - sizeof(parm_buf)); + phdr = sctp_get_next_param(m, offset, ¶m_buf, + sizeof(param_buf)); } /* validate authentication required parameters */ if (got_random && got_hmacs) { diff --git a/freebsd/sys/netinet/sctp_constants.h b/freebsd/sys/netinet/sctp_constants.h index e779051d..dca34cc5 100644 --- a/freebsd/sys/netinet/sctp_constants.h +++ b/freebsd/sys/netinet/sctp_constants.h @@ -758,7 +758,7 @@ __FBSDID("$FreeBSD$"); #define SCTP_DEFAULT_SPLIT_POINT_MIN 2904 /* Maximum length of diagnostic information in error causes */ -#define SCTP_DIAG_INFO_LEN 64 +#define SCTP_DIAG_INFO_LEN 128 /* ABORT CODES and other tell-tale location * codes are generated by adding the below diff --git a/freebsd/sys/netinet/sctp_indata.c b/freebsd/sys/netinet/sctp_indata.c index 57f13a13..1924aeab 100644 --- a/freebsd/sys/netinet/sctp_indata.c +++ b/freebsd/sys/netinet/sctp_indata.c @@ -61,7 +61,7 @@ __FBSDID("$FreeBSD$"); * This will cause sctp_service_queues() to get called on the top entry in * the list. */ -static void +static uint32_t sctp_add_chk_to_control(struct sctp_queued_to_read *control, struct sctp_stream_in *strm, struct sctp_tcb *stcb, @@ -94,6 +94,8 @@ sctp_calc_rwnd(struct sctp_tcb *stcb, struct sctp_association *asoc) asoc->size_on_reasm_queue == 0 && asoc->size_on_all_streams == 0) { /* Full rwnd granted */ + KASSERT(asoc->cnt_on_reasm_queue == 0, ("cnt_on_reasm_queue is %u", asoc->cnt_on_reasm_queue)); + KASSERT(asoc->cnt_on_all_streams == 0, ("cnt_on_all_streams is %u", asoc->cnt_on_all_streams)); calc = max(SCTP_SB_LIMIT_RCV(stcb->sctp_socket), SCTP_MINIMAL_RWND); return (calc); } @@ -560,7 +562,15 @@ sctp_queue_data_to_stream(struct sctp_tcb *stcb, } /* EY it wont be queued if it could be delivered directly */ queue_needed = 0; - asoc->size_on_all_streams -= control->length; + if (asoc->size_on_all_streams >= control->length) { + asoc->size_on_all_streams -= control->length; + } else { +#ifdef INVARIANTS + panic("size_on_all_streams = %u smaller than control length %u", asoc->size_on_all_streams, control->length); +#else + asoc->size_on_all_streams = 0; +#endif + } sctp_ucount_decr(asoc->cnt_on_all_streams); strm->last_mid_delivered++; sctp_mark_non_revokable(asoc, control->sinfo_tsn); @@ -573,10 +583,18 @@ sctp_queue_data_to_stream(struct sctp_tcb *stcb, nxt_todel = strm->last_mid_delivered + 1; if (SCTP_MID_EQ(asoc->idata_supported, nxt_todel, control->mid) && (((control->sinfo_flags >> 8) & SCTP_DATA_NOT_FRAG) == SCTP_DATA_NOT_FRAG)) { - asoc->size_on_all_streams -= control->length; - sctp_ucount_decr(asoc->cnt_on_all_streams); if (control->on_strm_q == SCTP_ON_ORDERED) { TAILQ_REMOVE(&strm->inqueue, control, next_instrm); + if (asoc->size_on_all_streams >= control->length) { + asoc->size_on_all_streams -= control->length; + } else { +#ifdef INVARIANTS + panic("size_on_all_streams = %u smaller than control length %u", asoc->size_on_all_streams, control->length); +#else + asoc->size_on_all_streams = 0; +#endif + } + sctp_ucount_decr(asoc->cnt_on_all_streams); #ifdef INVARIANTS } else { panic("Huh control: %p is on_strm_q: %d", @@ -673,7 +691,7 @@ sctp_setup_tail_pointer(struct sctp_queued_to_read *control) } static void -sctp_add_to_tail_pointer(struct sctp_queued_to_read *control, struct mbuf *m) +sctp_add_to_tail_pointer(struct sctp_queued_to_read *control, struct mbuf *m, uint32_t *added) { struct mbuf *prev = NULL; struct sctp_tcb *stcb; @@ -717,6 +735,7 @@ sctp_add_to_tail_pointer(struct sctp_queued_to_read *control, struct mbuf *m) */ sctp_sballoc(stcb, &stcb->sctp_socket->so_rcv, m); } + *added += SCTP_BUF_LEN(m); atomic_add_int(&control->length, SCTP_BUF_LEN(m)); m = SCTP_BUF_NEXT(m); } @@ -817,7 +836,15 @@ restart: tchk = TAILQ_FIRST(&control->reasm); if (tchk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) { TAILQ_REMOVE(&control->reasm, tchk, sctp_next); - asoc->size_on_reasm_queue -= tchk->send_size; + if (asoc->size_on_reasm_queue >= tchk->send_size) { + asoc->size_on_reasm_queue -= tchk->send_size; + } else { +#ifdef INVARIANTS + panic("size_on_reasm_queue = %u smaller than chunk length %u", asoc->size_on_reasm_queue, tchk->send_size); +#else + asoc->size_on_reasm_queue = 0; +#endif + } sctp_ucount_decr(asoc->cnt_on_reasm_queue); nc->first_frag_seen = 1; nc->fsn_included = tchk->rec.data.fsn; @@ -1129,6 +1156,16 @@ done_un: #endif SCTP_STAT_INCR_COUNTER64(sctps_reasmusrmsgs); TAILQ_REMOVE(&strm->inqueue, control, next_instrm); + if (asoc->size_on_all_streams >= control->length) { + asoc->size_on_all_streams -= control->length; + } else { +#ifdef INVARIANTS + panic("size_on_all_streams = %u smaller than control length %u", asoc->size_on_all_streams, control->length); +#else + asoc->size_on_all_streams = 0; +#endif + } + sctp_ucount_decr(asoc->cnt_on_all_streams); control->on_strm_q = 0; } if (strm->pd_api_started && control->pdapi_started) { @@ -1175,6 +1212,16 @@ deliver_more: #endif SCTP_STAT_INCR_COUNTER64(sctps_reasmusrmsgs); TAILQ_REMOVE(&strm->inqueue, control, next_instrm); + if (asoc->size_on_all_streams >= control->length) { + asoc->size_on_all_streams -= control->length; + } else { +#ifdef INVARIANTS + panic("size_on_all_streams = %u smaller than control length %u", asoc->size_on_all_streams, control->length); +#else + asoc->size_on_all_streams = 0; +#endif + } + sctp_ucount_decr(asoc->cnt_on_all_streams); control->on_strm_q = 0; } ret++; @@ -1221,7 +1268,7 @@ out: } -void +uint32_t sctp_add_chk_to_control(struct sctp_queued_to_read *control, struct sctp_stream_in *strm, struct sctp_tcb *stcb, struct sctp_association *asoc, @@ -1231,6 +1278,7 @@ sctp_add_chk_to_control(struct sctp_queued_to_read *control, * Given a control and a chunk, merge the data from the chk onto the * control and free up the chunk resources. */ + uint32_t added = 0; int i_locked = 0; if (control->on_read_q && (hold_rlock == 0)) { @@ -1244,7 +1292,7 @@ sctp_add_chk_to_control(struct sctp_queued_to_read *control, control->data = chk->data; sctp_setup_tail_pointer(control); } else { - sctp_add_to_tail_pointer(control, chk->data); + sctp_add_to_tail_pointer(control, chk->data, &added); } control->fsn_included = chk->rec.data.fsn; asoc->size_on_reasm_queue -= chk->send_size; @@ -1270,6 +1318,16 @@ sctp_add_chk_to_control(struct sctp_queued_to_read *control, } else if (control->on_strm_q == SCTP_ON_ORDERED) { /* Ordered */ TAILQ_REMOVE(&strm->inqueue, control, next_instrm); + if (asoc->size_on_all_streams >= control->length) { + asoc->size_on_all_streams -= control->length; + } else { +#ifdef INVARIANTS + panic("size_on_all_streams = %u smaller than control length %u", asoc->size_on_all_streams, control->length); +#else + asoc->size_on_all_streams = 0; +#endif + } + sctp_ucount_decr(asoc->cnt_on_all_streams); control->on_strm_q = 0; #ifdef INVARIANTS } else if (control->on_strm_q) { @@ -1285,6 +1343,7 @@ sctp_add_chk_to_control(struct sctp_queued_to_read *control, SCTP_INP_READ_UNLOCK(stcb->sctp_ep); } sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED); + return (added); } /* @@ -1304,6 +1363,7 @@ sctp_queue_data_for_reasm(struct sctp_tcb *stcb, struct sctp_association *asoc, struct sctp_tmit_chunk *at, *nat; struct sctp_stream_in *strm; int do_wakeup, unordered; + uint32_t lenadded; strm = &asoc->strmin[control->sinfo_stream]; /* @@ -1316,6 +1376,9 @@ sctp_queue_data_for_reasm(struct sctp_tcb *stcb, struct sctp_association *asoc, } /* Must be added to the stream-in queue */ if (created_control) { + if (unordered == 0) { + sctp_ucount_incr(asoc->cnt_on_all_streams); + } if (sctp_place_control_in_stream(strm, asoc, control)) { /* Duplicate SSN? */ sctp_clean_up_control(stcb, control); @@ -1375,6 +1438,7 @@ sctp_queue_data_for_reasm(struct sctp_tcb *stcb, struct sctp_association *asoc, chk->data = NULL; sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED); sctp_setup_tail_pointer(control); + asoc->size_on_all_streams += control->length; } else { /* Place the chunk in our list */ int inserted = 0; @@ -1531,7 +1595,8 @@ sctp_queue_data_for_reasm(struct sctp_tcb *stcb, struct sctp_association *asoc, at->rec.data.fsn, next_fsn, control->fsn_included); TAILQ_REMOVE(&control->reasm, at, sctp_next); - sctp_add_chk_to_control(control, strm, stcb, asoc, at, SCTP_READ_LOCK_NOT_HELD); + lenadded = sctp_add_chk_to_control(control, strm, stcb, asoc, at, SCTP_READ_LOCK_NOT_HELD); + asoc->size_on_all_streams += lenadded; if (control->on_read_q) { do_wakeup = 1; } @@ -1602,7 +1667,7 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc, uint16_t sid; struct mbuf *op_err; char msg[SCTP_DIAG_INFO_LEN]; - struct sctp_queued_to_read *control = NULL; + struct sctp_queued_to_read *control, *ncontrol; uint32_t ppid; uint8_t chk_flags; struct sctp_stream_reset_list *liste; @@ -2008,7 +2073,12 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc, return (0); } if ((chk_flags & SCTP_DATA_NOT_FRAG) == SCTP_DATA_NOT_FRAG) { + struct mbuf *mm; + control->data = dmbuf; + for (mm = control->data; mm; mm = mm->m_next) { + control->length += SCTP_BUF_LEN(mm); + } control->tail_mbuf = NULL; control->end_added = 1; control->last_frag_seen = 1; @@ -2118,16 +2188,16 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc, /* first one on */ TAILQ_INSERT_TAIL(&asoc->pending_reply_queue, control, next); } else { - struct sctp_queued_to_read *ctlOn, *nctlOn; + struct sctp_queued_to_read *lcontrol, *nlcontrol; unsigned char inserted = 0; - TAILQ_FOREACH_SAFE(ctlOn, &asoc->pending_reply_queue, next, nctlOn) { - if (SCTP_TSN_GT(control->sinfo_tsn, ctlOn->sinfo_tsn)) { + TAILQ_FOREACH_SAFE(lcontrol, &asoc->pending_reply_queue, next, nlcontrol) { + if (SCTP_TSN_GT(control->sinfo_tsn, lcontrol->sinfo_tsn)) { continue; } else { /* found it */ - TAILQ_INSERT_BEFORE(ctlOn, control, next); + TAILQ_INSERT_BEFORE(lcontrol, control, next); inserted = 1; break; } @@ -2218,8 +2288,6 @@ finish_express_del: * pending_reply space 3: distribute any chunks in * pending_reply_queue. */ - struct sctp_queued_to_read *ctl, *nctl; - sctp_reset_in_stream(stcb, liste->number_entries, liste->list_of_streams); TAILQ_REMOVE(&asoc->resetHead, liste, next_resp); sctp_send_deferred_reset_response(stcb, liste, SCTP_STREAM_RESET_RESULT_PERFORMED); @@ -2228,34 +2296,34 @@ finish_express_del: liste = TAILQ_FIRST(&asoc->resetHead); if (TAILQ_EMPTY(&asoc->resetHead)) { /* All can be removed */ - TAILQ_FOREACH_SAFE(ctl, &asoc->pending_reply_queue, next, nctl) { - TAILQ_REMOVE(&asoc->pending_reply_queue, ctl, next); - sctp_queue_data_to_stream(stcb, asoc, ctl, abort_flag, &need_reasm_check); + TAILQ_FOREACH_SAFE(control, &asoc->pending_reply_queue, next, ncontrol) { + TAILQ_REMOVE(&asoc->pending_reply_queue, control, next); + sctp_queue_data_to_stream(stcb, asoc, control, abort_flag, &need_reasm_check); if (*abort_flag) { return (0); } if (need_reasm_check) { - (void)sctp_deliver_reasm_check(stcb, asoc, &asoc->strmin[ctl->sinfo_stream], SCTP_READ_LOCK_NOT_HELD); + (void)sctp_deliver_reasm_check(stcb, asoc, &asoc->strmin[control->sinfo_stream], SCTP_READ_LOCK_NOT_HELD); need_reasm_check = 0; } } } else { - TAILQ_FOREACH_SAFE(ctl, &asoc->pending_reply_queue, next, nctl) { - if (SCTP_TSN_GT(ctl->sinfo_tsn, liste->tsn)) { + TAILQ_FOREACH_SAFE(control, &asoc->pending_reply_queue, next, ncontrol) { + if (SCTP_TSN_GT(control->sinfo_tsn, liste->tsn)) { break; } /* - * if ctl->sinfo_tsn is <= liste->tsn we can - * process it which is the NOT of - * ctl->sinfo_tsn > liste->tsn + * if control->sinfo_tsn is <= liste->tsn we + * can process it which is the NOT of + * control->sinfo_tsn > liste->tsn */ - TAILQ_REMOVE(&asoc->pending_reply_queue, ctl, next); - sctp_queue_data_to_stream(stcb, asoc, ctl, abort_flag, &need_reasm_check); + TAILQ_REMOVE(&asoc->pending_reply_queue, control, next); + sctp_queue_data_to_stream(stcb, asoc, control, abort_flag, &need_reasm_check); if (*abort_flag) { return (0); } if (need_reasm_check) { - (void)sctp_deliver_reasm_check(stcb, asoc, &asoc->strmin[ctl->sinfo_stream], SCTP_READ_LOCK_NOT_HELD); + (void)sctp_deliver_reasm_check(stcb, asoc, &asoc->strmin[control->sinfo_stream], SCTP_READ_LOCK_NOT_HELD); need_reasm_check = 0; } } @@ -4226,47 +4294,44 @@ again: ((*asoc->ss_functions.sctp_ss_is_user_msgs_incomplete) (stcb, asoc))) { asoc->state |= SCTP_STATE_PARTIAL_MSG_LEFT; } + if (((asoc->state & SCTP_STATE_SHUTDOWN_PENDING) || + (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) && + (asoc->stream_queue_cnt == 1) && + (asoc->state & SCTP_STATE_PARTIAL_MSG_LEFT)) { + struct mbuf *op_err; + + *abort_now = 1; + /* XXX */ + op_err = sctp_generate_cause(SCTP_CAUSE_USER_INITIATED_ABT, ""); + stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_24; + sctp_abort_an_association(stcb->sctp_ep, stcb, op_err, SCTP_SO_NOT_LOCKED); + return; + } if ((asoc->state & SCTP_STATE_SHUTDOWN_PENDING) && (asoc->stream_queue_cnt == 0)) { - if (asoc->state & SCTP_STATE_PARTIAL_MSG_LEFT) { - /* Need to abort here */ - struct mbuf *op_err; + struct sctp_nets *netp; - abort_out_now: - *abort_now = 1; - /* XXX */ - op_err = sctp_generate_cause(SCTP_CAUSE_USER_INITIATED_ABT, ""); - stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_24; - sctp_abort_an_association(stcb->sctp_ep, stcb, op_err, SCTP_SO_NOT_LOCKED); - return; + if ((SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) || + (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { + SCTP_STAT_DECR_GAUGE32(sctps_currestab); + } + SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_SENT); + SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING); + sctp_stop_timers_for_shutdown(stcb); + if (asoc->alternate) { + netp = asoc->alternate; } else { - struct sctp_nets *netp; - - if ((SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) || - (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { - SCTP_STAT_DECR_GAUGE32(sctps_currestab); - } - SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_SENT); - SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING); - sctp_stop_timers_for_shutdown(stcb); - if (asoc->alternate) { - netp = asoc->alternate; - } else { - netp = asoc->primary_destination; - } - sctp_send_shutdown(stcb, netp); - sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, - stcb->sctp_ep, stcb, netp); - sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, - stcb->sctp_ep, stcb, netp); + netp = asoc->primary_destination; } + sctp_send_shutdown(stcb, netp); + sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, + stcb->sctp_ep, stcb, netp); + sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, + stcb->sctp_ep, stcb, netp); } else if ((SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED) && (asoc->stream_queue_cnt == 0)) { struct sctp_nets *netp; - if (asoc->state & SCTP_STATE_PARTIAL_MSG_LEFT) { - goto abort_out_now; - } SCTP_STAT_DECR_GAUGE32(sctps_currestab); SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_ACK_SENT); SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING); @@ -4922,48 +4987,45 @@ hopeless_peer: ((*asoc->ss_functions.sctp_ss_is_user_msgs_incomplete) (stcb, asoc))) { asoc->state |= SCTP_STATE_PARTIAL_MSG_LEFT; } + if (((asoc->state & SCTP_STATE_SHUTDOWN_PENDING) || + (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) && + (asoc->stream_queue_cnt == 1) && + (asoc->state & SCTP_STATE_PARTIAL_MSG_LEFT)) { + struct mbuf *op_err; + + *abort_now = 1; + /* XXX */ + op_err = sctp_generate_cause(SCTP_CAUSE_USER_INITIATED_ABT, ""); + stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_24; + sctp_abort_an_association(stcb->sctp_ep, stcb, op_err, SCTP_SO_NOT_LOCKED); + return; + } if ((asoc->state & SCTP_STATE_SHUTDOWN_PENDING) && (asoc->stream_queue_cnt == 0)) { - if (asoc->state & SCTP_STATE_PARTIAL_MSG_LEFT) { - /* Need to abort here */ - struct mbuf *op_err; + struct sctp_nets *netp; - abort_out_now: - *abort_now = 1; - /* XXX */ - op_err = sctp_generate_cause(SCTP_CAUSE_USER_INITIATED_ABT, ""); - stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_31; - sctp_abort_an_association(stcb->sctp_ep, stcb, op_err, SCTP_SO_NOT_LOCKED); - return; + if ((SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) || + (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { + SCTP_STAT_DECR_GAUGE32(sctps_currestab); + } + SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_SENT); + SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING); + sctp_stop_timers_for_shutdown(stcb); + if (asoc->alternate) { + netp = asoc->alternate; } else { - struct sctp_nets *netp; - - if ((SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) || - (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { - SCTP_STAT_DECR_GAUGE32(sctps_currestab); - } - SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_SENT); - SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING); - sctp_stop_timers_for_shutdown(stcb); - if (asoc->alternate) { - netp = asoc->alternate; - } else { - netp = asoc->primary_destination; - } - sctp_send_shutdown(stcb, netp); - sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, - stcb->sctp_ep, stcb, netp); - sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, - stcb->sctp_ep, stcb, netp); + netp = asoc->primary_destination; } + sctp_send_shutdown(stcb, netp); + sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, + stcb->sctp_ep, stcb, netp); + sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, + stcb->sctp_ep, stcb, netp); return; } else if ((SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED) && (asoc->stream_queue_cnt == 0)) { struct sctp_nets *netp; - if (asoc->state & SCTP_STATE_PARTIAL_MSG_LEFT) { - goto abort_out_now; - } SCTP_STAT_DECR_GAUGE32(sctps_currestab); SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_ACK_SENT); SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING); @@ -5183,7 +5245,7 @@ static void sctp_kick_prsctp_reorder_queue(struct sctp_tcb *stcb, struct sctp_stream_in *strmin) { - struct sctp_queued_to_read *ctl, *nctl; + struct sctp_queued_to_read *control, *ncontrol; struct sctp_association *asoc; uint32_t mid; int need_reasm_check = 0; @@ -5194,43 +5256,51 @@ sctp_kick_prsctp_reorder_queue(struct sctp_tcb *stcb, * First deliver anything prior to and including the stream no that * came in. */ - TAILQ_FOREACH_SAFE(ctl, &strmin->inqueue, next_instrm, nctl) { - if (SCTP_MID_GE(asoc->idata_supported, mid, ctl->mid)) { + TAILQ_FOREACH_SAFE(control, &strmin->inqueue, next_instrm, ncontrol) { + if (SCTP_MID_GE(asoc->idata_supported, mid, control->mid)) { /* this is deliverable now */ - if (((ctl->sinfo_flags >> 8) & SCTP_DATA_NOT_FRAG) == SCTP_DATA_NOT_FRAG) { - if (ctl->on_strm_q) { - if (ctl->on_strm_q == SCTP_ON_ORDERED) { - TAILQ_REMOVE(&strmin->inqueue, ctl, next_instrm); - } else if (ctl->on_strm_q == SCTP_ON_UNORDERED) { - TAILQ_REMOVE(&strmin->uno_inqueue, ctl, next_instrm); + if (((control->sinfo_flags >> 8) & SCTP_DATA_NOT_FRAG) == SCTP_DATA_NOT_FRAG) { + if (control->on_strm_q) { + if (control->on_strm_q == SCTP_ON_ORDERED) { + TAILQ_REMOVE(&strmin->inqueue, control, next_instrm); + } else if (control->on_strm_q == SCTP_ON_UNORDERED) { + TAILQ_REMOVE(&strmin->uno_inqueue, control, next_instrm); #ifdef INVARIANTS } else { panic("strmin: %p ctl: %p unknown %d", - strmin, ctl, ctl->on_strm_q); + strmin, control, control->on_strm_q); #endif } - ctl->on_strm_q = 0; + control->on_strm_q = 0; } /* subtract pending on streams */ - asoc->size_on_all_streams -= ctl->length; + if (asoc->size_on_all_streams >= control->length) { + asoc->size_on_all_streams -= control->length; + } else { +#ifdef INVARIANTS + panic("size_on_all_streams = %u smaller than control length %u", asoc->size_on_all_streams, control->length); +#else + asoc->size_on_all_streams = 0; +#endif + } sctp_ucount_decr(asoc->cnt_on_all_streams); /* deliver it to at least the delivery-q */ if (stcb->sctp_socket) { - sctp_mark_non_revokable(asoc, ctl->sinfo_tsn); + sctp_mark_non_revokable(asoc, control->sinfo_tsn); sctp_add_to_readq(stcb->sctp_ep, stcb, - ctl, + control, &stcb->sctp_socket->so_rcv, 1, SCTP_READ_LOCK_HELD, SCTP_SO_NOT_LOCKED); } } else { /* Its a fragmented message */ - if (ctl->first_frag_seen) { + if (control->first_frag_seen) { /* * Make it so this is next to * deliver, we restore later */ - strmin->last_mid_delivered = ctl->mid - 1; + strmin->last_mid_delivered = control->mid - 1; need_reasm_check = 1; break; } @@ -5259,32 +5329,40 @@ sctp_kick_prsctp_reorder_queue(struct sctp_tcb *stcb, * now ready. */ mid = strmin->last_mid_delivered + 1; - TAILQ_FOREACH_SAFE(ctl, &strmin->inqueue, next_instrm, nctl) { - if (SCTP_MID_EQ(asoc->idata_supported, mid, ctl->mid)) { - if (((ctl->sinfo_flags >> 8) & SCTP_DATA_NOT_FRAG) == SCTP_DATA_NOT_FRAG) { + TAILQ_FOREACH_SAFE(control, &strmin->inqueue, next_instrm, ncontrol) { + if (SCTP_MID_EQ(asoc->idata_supported, mid, control->mid)) { + if (((control->sinfo_flags >> 8) & SCTP_DATA_NOT_FRAG) == SCTP_DATA_NOT_FRAG) { /* this is deliverable now */ - if (ctl->on_strm_q) { - if (ctl->on_strm_q == SCTP_ON_ORDERED) { - TAILQ_REMOVE(&strmin->inqueue, ctl, next_instrm); - } else if (ctl->on_strm_q == SCTP_ON_UNORDERED) { - TAILQ_REMOVE(&strmin->uno_inqueue, ctl, next_instrm); + if (control->on_strm_q) { + if (control->on_strm_q == SCTP_ON_ORDERED) { + TAILQ_REMOVE(&strmin->inqueue, control, next_instrm); + } else if (control->on_strm_q == SCTP_ON_UNORDERED) { + TAILQ_REMOVE(&strmin->uno_inqueue, control, next_instrm); #ifdef INVARIANTS } else { panic("strmin: %p ctl: %p unknown %d", - strmin, ctl, ctl->on_strm_q); + strmin, control, control->on_strm_q); #endif } - ctl->on_strm_q = 0; + control->on_strm_q = 0; } /* subtract pending on streams */ - asoc->size_on_all_streams -= ctl->length; + if (asoc->size_on_all_streams >= control->length) { + asoc->size_on_all_streams -= control->length; + } else { +#ifdef INVARIANTS + panic("size_on_all_streams = %u smaller than control length %u", asoc->size_on_all_streams, control->length); +#else + asoc->size_on_all_streams = 0; +#endif + } sctp_ucount_decr(asoc->cnt_on_all_streams); /* deliver it to at least the delivery-q */ - strmin->last_mid_delivered = ctl->mid; + strmin->last_mid_delivered = control->mid; if (stcb->sctp_socket) { - sctp_mark_non_revokable(asoc, ctl->sinfo_tsn); + sctp_mark_non_revokable(asoc, control->sinfo_tsn); sctp_add_to_readq(stcb->sctp_ep, stcb, - ctl, + control, &stcb->sctp_socket->so_rcv, 1, SCTP_READ_LOCK_HELD, SCTP_SO_NOT_LOCKED); @@ -5292,12 +5370,12 @@ sctp_kick_prsctp_reorder_queue(struct sctp_tcb *stcb, mid = strmin->last_mid_delivered + 1; } else { /* Its a fragmented message */ - if (ctl->first_frag_seen) { + if (control->first_frag_seen) { /* * Make it so this is next to * deliver */ - strmin->last_mid_delivered = ctl->mid - 1; + strmin->last_mid_delivered = control->mid - 1; need_reasm_check = 1; break; } @@ -5349,7 +5427,15 @@ sctp_flush_reassm_for_str_seq(struct sctp_tcb *stcb, } cnt_removed++; TAILQ_REMOVE(&control->reasm, chk, sctp_next); - asoc->size_on_reasm_queue -= chk->send_size; + if (asoc->size_on_reasm_queue >= chk->send_size) { + asoc->size_on_reasm_queue -= chk->send_size; + } else { +#ifdef INVARIANTS + panic("size_on_reasm_queue = %u smaller than chunk length %u", asoc->size_on_reasm_queue, chk->send_size); +#else + asoc->size_on_reasm_queue = 0; +#endif + } sctp_ucount_decr(asoc->cnt_on_reasm_queue); if (chk->data) { sctp_m_freem(chk->data); @@ -5375,6 +5461,16 @@ sctp_flush_reassm_for_str_seq(struct sctp_tcb *stcb, } if (control->on_strm_q == SCTP_ON_ORDERED) { TAILQ_REMOVE(&strm->inqueue, control, next_instrm); + if (asoc->size_on_all_streams >= control->length) { + asoc->size_on_all_streams -= control->length; + } else { +#ifdef INVARIANTS + panic("size_on_all_streams = %u smaller than control length %u", asoc->size_on_all_streams, control->length); +#else + asoc->size_on_all_streams = 0; +#endif + } + sctp_ucount_decr(asoc->cnt_on_all_streams); control->on_strm_q = 0; } else if (control->on_strm_q == SCTP_ON_UNORDERED) { TAILQ_REMOVE(&strm->uno_inqueue, control, next_instrm); @@ -5418,7 +5514,7 @@ sctp_handle_forward_tsn(struct sctp_tcb *stcb, unsigned int i, fwd_sz, m_size; uint32_t str_seq; struct sctp_stream_in *strm; - struct sctp_queued_to_read *ctl, *sv; + struct sctp_queued_to_read *control, *sv; asoc = &stcb->asoc; if ((fwd_sz = ntohs(fwd->ch.chunk_length)) < sizeof(struct sctp_forward_tsn_chunk)) { @@ -5577,25 +5673,35 @@ sctp_handle_forward_tsn(struct sctp_tcb *stcb, for (cur_mid = strm->last_mid_delivered; SCTP_MID_GE(asoc->idata_supported, mid, cur_mid); cur_mid++) { sctp_flush_reassm_for_str_seq(stcb, asoc, sid, cur_mid, ordered, new_cum_tsn); } - TAILQ_FOREACH(ctl, &stcb->sctp_ep->read_queue, next) { - if ((ctl->sinfo_stream == sid) && - (SCTP_MID_EQ(asoc->idata_supported, ctl->mid, mid))) { + TAILQ_FOREACH(control, &stcb->sctp_ep->read_queue, next) { + if ((control->sinfo_stream == sid) && + (SCTP_MID_EQ(asoc->idata_supported, control->mid, mid))) { str_seq = (sid << 16) | (0x0000ffff & mid); - ctl->pdapi_aborted = 1; + control->pdapi_aborted = 1; sv = stcb->asoc.control_pdapi; - ctl->end_added = 1; - if (ctl->on_strm_q == SCTP_ON_ORDERED) { - TAILQ_REMOVE(&strm->inqueue, ctl, next_instrm); - } else if (ctl->on_strm_q == SCTP_ON_UNORDERED) { - TAILQ_REMOVE(&strm->uno_inqueue, ctl, next_instrm); + control->end_added = 1; + if (control->on_strm_q == SCTP_ON_ORDERED) { + TAILQ_REMOVE(&strm->inqueue, control, next_instrm); + if (asoc->size_on_all_streams >= control->length) { + asoc->size_on_all_streams -= control->length; + } else { #ifdef INVARIANTS - } else if (ctl->on_strm_q) { + panic("size_on_all_streams = %u smaller than control length %u", asoc->size_on_all_streams, control->length); +#else + asoc->size_on_all_streams = 0; +#endif + } + sctp_ucount_decr(asoc->cnt_on_all_streams); + } else if (control->on_strm_q == SCTP_ON_UNORDERED) { + TAILQ_REMOVE(&strm->uno_inqueue, control, next_instrm); +#ifdef INVARIANTS + } else if (control->on_strm_q) { panic("strm: %p ctl: %p unknown %d", - strm, ctl, ctl->on_strm_q); + strm, control, control->on_strm_q); #endif } - ctl->on_strm_q = 0; - stcb->asoc.control_pdapi = ctl; + control->on_strm_q = 0; + stcb->asoc.control_pdapi = control; sctp_ulp_notify(SCTP_NOTIFY_PARTIAL_DELVIERY_INDICATION, stcb, SCTP_PARTIAL_DELIVERY_ABORTED, @@ -5603,8 +5709,8 @@ sctp_handle_forward_tsn(struct sctp_tcb *stcb, SCTP_SO_NOT_LOCKED); stcb->asoc.control_pdapi = sv; break; - } else if ((ctl->sinfo_stream == sid) && - SCTP_MID_GT(asoc->idata_supported, ctl->mid, mid)) { + } else if ((control->sinfo_stream == sid) && + SCTP_MID_GT(asoc->idata_supported, control->mid, mid)) { /* We are past our victim SSN */ break; } diff --git a/freebsd/sys/netinet/sctp_input.c b/freebsd/sys/netinet/sctp_input.c index be01c38a..2e844fb5 100644 --- a/freebsd/sys/netinet/sctp_input.c +++ b/freebsd/sys/netinet/sctp_input.c @@ -2151,23 +2151,23 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset, * cookie was in flight. Only recourse is to abort the * association. */ - atomic_add_int(&stcb->asoc.refcnt, 1); op_err = sctp_generate_cause(SCTP_CAUSE_OUT_OF_RESC, ""); sctp_abort_association(inp, (struct sctp_tcb *)NULL, m, iphlen, src, dst, sh, op_err, mflowtype, mflowid, vrf_id, port); #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) + atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); SCTP_SOCKET_LOCK(so, 1); SCTP_TCB_LOCK(stcb); + atomic_subtract_int(&stcb->asoc.refcnt, 1); #endif (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT + SCTP_LOC_18); #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) SCTP_SOCKET_UNLOCK(so, 1); #endif - atomic_subtract_int(&stcb->asoc.refcnt, 1); return (NULL); } /* process the INIT-ACK info (my info) */ @@ -2188,36 +2188,36 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset, else retval = 0; if (retval < 0) { - atomic_add_int(&stcb->asoc.refcnt, 1); #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) + atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); SCTP_SOCKET_LOCK(so, 1); SCTP_TCB_LOCK(stcb); + atomic_subtract_int(&stcb->asoc.refcnt, 1); #endif (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT + SCTP_LOC_19); #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) SCTP_SOCKET_UNLOCK(so, 1); #endif - atomic_subtract_int(&stcb->asoc.refcnt, 1); return (NULL); } /* load all addresses */ if (sctp_load_addresses_from_init(stcb, m, init_offset + sizeof(struct sctp_init_chunk), initack_offset, src, dst, init_src, port)) { - atomic_add_int(&stcb->asoc.refcnt, 1); #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) + atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); SCTP_SOCKET_LOCK(so, 1); SCTP_TCB_LOCK(stcb); + atomic_subtract_int(&stcb->asoc.refcnt, 1); #endif (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT + SCTP_LOC_20); #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) SCTP_SOCKET_UNLOCK(so, 1); #endif - atomic_subtract_int(&stcb->asoc.refcnt, 1); return (NULL); } /* @@ -2236,35 +2236,24 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset, /* auth HMAC failed, dump the assoc and packet */ SCTPDBG(SCTP_DEBUG_AUTH1, "COOKIE-ECHO: AUTH failed\n"); - atomic_add_int(&stcb->asoc.refcnt, 1); #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) + atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); SCTP_SOCKET_LOCK(so, 1); SCTP_TCB_LOCK(stcb); + atomic_subtract_int(&stcb->asoc.refcnt, 1); #endif (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT + SCTP_LOC_21); #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) SCTP_SOCKET_UNLOCK(so, 1); #endif - atomic_subtract_int(&stcb->asoc.refcnt, 1); return (NULL); } else { /* remaining chunks checked... good to go */ stcb->asoc.authenticated = 1; } } - /* update current state */ - SCTPDBG(SCTP_DEBUG_INPUT2, "moving to OPEN state\n"); - SCTP_SET_STATE(asoc, SCTP_STATE_OPEN); - if (asoc->state & SCTP_STATE_SHUTDOWN_PENDING) { - sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, - stcb->sctp_ep, stcb, asoc->primary_destination); - } - sctp_stop_all_cookie_timers(stcb); - SCTP_STAT_INCR_COUNTER32(sctps_passiveestab); - SCTP_STAT_INCR_GAUGE32(sctps_currestab); - /* * if we're doing ASCONFs, check to see if we have any new local * addresses that need to get added to the peer (eg. addresses @@ -2297,21 +2286,32 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset, break; #endif default: - atomic_add_int(&stcb->asoc.refcnt, 1); #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) + atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); SCTP_SOCKET_LOCK(so, 1); SCTP_TCB_LOCK(stcb); + atomic_subtract_int(&stcb->asoc.refcnt, 1); #endif (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT + SCTP_LOC_22); #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) SCTP_SOCKET_UNLOCK(so, 1); #endif - atomic_subtract_int(&stcb->asoc.refcnt, 1); return (NULL); } + /* update current state */ + SCTPDBG(SCTP_DEBUG_INPUT2, "moving to OPEN state\n"); + SCTP_SET_STATE(asoc, SCTP_STATE_OPEN); + if (asoc->state & SCTP_STATE_SHUTDOWN_PENDING) { + sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, + stcb->sctp_ep, stcb, asoc->primary_destination); + } + sctp_stop_all_cookie_timers(stcb); + SCTP_STAT_INCR_COUNTER32(sctps_passiveestab); + SCTP_STAT_INCR_GAUGE32(sctps_currestab); + /* set up to notify upper layer */ *notification = SCTP_NOTIFY_ASSOC_UP; if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || @@ -2443,6 +2443,12 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, cookie_offset = offset + sizeof(struct sctp_chunkhdr); cookie_len = ntohs(cp->ch.chunk_length); + if (cookie_len < sizeof(struct sctp_cookie_echo_chunk) + + sizeof(struct sctp_init_chunk) + + sizeof(struct sctp_init_ack_chunk) + SCTP_SIGNATURE_SIZE) { + /* cookie too small */ + return (NULL); + } if ((cookie->peerport != sh->src_port) || (cookie->myport != sh->dest_port) || (cookie->my_vtag != sh->v_tag)) { @@ -2455,12 +2461,6 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, */ return (NULL); } - if (cookie_len < sizeof(struct sctp_cookie_echo_chunk) + - sizeof(struct sctp_init_chunk) + - sizeof(struct sctp_init_ack_chunk) + SCTP_SIGNATURE_SIZE) { - /* cookie too small */ - return (NULL); - } /* * split off the signature into its own mbuf (since it should not be * calculated in the sctp_hmac_m() call). @@ -3619,7 +3619,7 @@ sctp_handle_stream_reset_response(struct sctp_tcb *stcb, struct sctp_stream_reset_response *respin) { uint16_t type; - int lparm_len; + int lparam_len; struct sctp_association *asoc = &stcb->asoc; struct sctp_tmit_chunk *chk; struct sctp_stream_reset_request *req_param; @@ -3636,12 +3636,12 @@ sctp_handle_stream_reset_response(struct sctp_tcb *stcb, if (req_param != NULL) { stcb->asoc.str_reset_seq_out++; type = ntohs(req_param->ph.param_type); - lparm_len = ntohs(req_param->ph.param_length); + lparam_len = ntohs(req_param->ph.param_length); if (type == SCTP_STR_RESET_OUT_REQUEST) { int no_clear = 0; req_out_param = (struct sctp_stream_reset_out_request *)req_param; - number_entries = (lparm_len - sizeof(struct sctp_stream_reset_out_request)) / sizeof(uint16_t); + number_entries = (lparam_len - sizeof(struct sctp_stream_reset_out_request)) / sizeof(uint16_t); asoc->stream_reset_out_is_outstanding = 0; if (asoc->stream_reset_outstanding) asoc->stream_reset_outstanding--; @@ -3667,7 +3667,7 @@ sctp_handle_stream_reset_response(struct sctp_tcb *stcb, } } else if (type == SCTP_STR_RESET_IN_REQUEST) { req_in_param = (struct sctp_stream_reset_in_request *)req_param; - number_entries = (lparm_len - sizeof(struct sctp_stream_reset_in_request)) / sizeof(uint16_t); + number_entries = (lparam_len - sizeof(struct sctp_stream_reset_in_request)) / sizeof(uint16_t); if (asoc->stream_reset_outstanding) asoc->stream_reset_outstanding--; if (action == SCTP_STREAM_RESET_RESULT_DENIED) { @@ -5196,17 +5196,27 @@ process_control_chunks: return (NULL); } } - /* + /*- * First are we accepting? We do this again here * since it is possible that a previous endpoint WAS * listening responded to a INIT-ACK and then * closed. We opened and bound.. and are now no * longer listening. + * + * XXXGL: notes on checking listen queue length. + * 1) SCTP_IS_LISTENING() doesn't necessarily mean + * SOLISTENING(), because a listening "UDP type" + * socket isn't listening in terms of the socket + * layer. It is a normal data flow socket, that + * can fork off new connections. Thus, we should + * look into sol_qlen only in case we are !UDP. + * 2) Checking sol_qlen in general requires locking + * the socket, and this code lacks that. */ - if ((stcb == NULL) && (!SCTP_IS_LISTENING(inp) || - inp->sctp_socket->so_qlen >= inp->sctp_socket->so_qlimit)) { + (!(inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) && + inp->sctp_socket->sol_qlen >= inp->sctp_socket->sol_qlimit))) { if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) && (SCTP_BASE_SYSCTL(sctp_abort_if_one_2_one_hits_limit))) { op_err = sctp_generate_cause(SCTP_CAUSE_OUT_OF_RESC, ""); diff --git a/freebsd/sys/netinet/sctp_os_bsd.h b/freebsd/sys/netinet/sctp_os_bsd.h index f2bea00e..194a03ed 100644 --- a/freebsd/sys/netinet/sctp_os_bsd.h +++ b/freebsd/sys/netinet/sctp_os_bsd.h @@ -392,8 +392,8 @@ typedef struct callout sctp_os_timer_t; (sb).sb_mb = NULL; \ (sb).sb_mbcnt = 0; -#define SCTP_SB_LIMIT_RCV(so) so->so_rcv.sb_hiwat -#define SCTP_SB_LIMIT_SND(so) so->so_snd.sb_hiwat +#define SCTP_SB_LIMIT_RCV(so) (SOLISTENING(so) ? so->sol_sbrcv_hiwat : so->so_rcv.sb_hiwat) +#define SCTP_SB_LIMIT_SND(so) (SOLISTENING(so) ? so->sol_sbsnd_hiwat : so->so_snd.sb_hiwat) /* * routes, output, etc. diff --git a/freebsd/sys/netinet/sctp_output.c b/freebsd/sys/netinet/sctp_output.c index 221d0570..5e2fbbc2 100644 --- a/freebsd/sys/netinet/sctp_output.c +++ b/freebsd/sys/netinet/sctp_output.c @@ -1942,7 +1942,7 @@ static struct mbuf * sctp_add_addr_to_mbuf(struct mbuf *m, struct sctp_ifa *ifa, uint16_t *len) { #if defined(INET) || defined(INET6) - struct sctp_paramhdr *parmh; + struct sctp_paramhdr *paramh; struct mbuf *mret; uint16_t plen; #endif @@ -1964,7 +1964,7 @@ sctp_add_addr_to_mbuf(struct mbuf *m, struct sctp_ifa *ifa, uint16_t *len) #if defined(INET) || defined(INET6) if (M_TRAILINGSPACE(m) >= plen) { /* easy side we just drop it on the end */ - parmh = (struct sctp_paramhdr *)(SCTP_BUF_AT(m, SCTP_BUF_LEN(m))); + paramh = (struct sctp_paramhdr *)(SCTP_BUF_AT(m, SCTP_BUF_LEN(m))); mret = m; } else { /* Need more space */ @@ -1978,7 +1978,7 @@ sctp_add_addr_to_mbuf(struct mbuf *m, struct sctp_ifa *ifa, uint16_t *len) return (m); } mret = SCTP_BUF_NEXT(mret); - parmh = mtod(mret, struct sctp_paramhdr *); + paramh = mtod(mret, struct sctp_paramhdr *); } /* now add the parameter */ switch (ifa->address.sa.sa_family) { @@ -1989,9 +1989,9 @@ sctp_add_addr_to_mbuf(struct mbuf *m, struct sctp_ifa *ifa, uint16_t *len) struct sockaddr_in *sin; sin = &ifa->address.sin; - ipv4p = (struct sctp_ipv4addr_param *)parmh; - parmh->param_type = htons(SCTP_IPV4_ADDRESS); - parmh->param_length = htons(plen); + ipv4p = (struct sctp_ipv4addr_param *)paramh; + paramh->param_type = htons(SCTP_IPV4_ADDRESS); + paramh->param_length = htons(plen); ipv4p->addr = sin->sin_addr.s_addr; SCTP_BUF_LEN(mret) += plen; break; @@ -2004,9 +2004,9 @@ sctp_add_addr_to_mbuf(struct mbuf *m, struct sctp_ifa *ifa, uint16_t *len) struct sockaddr_in6 *sin6; sin6 = &ifa->address.sin6; - ipv6p = (struct sctp_ipv6addr_param *)parmh; - parmh->param_type = htons(SCTP_IPV6_ADDRESS); - parmh->param_length = htons(plen); + ipv6p = (struct sctp_ipv6addr_param *)paramh; + paramh->param_type = htons(SCTP_IPV6_ADDRESS); + paramh->param_length = htons(plen); memcpy(ipv6p->addr, &sin6->sin6_addr, sizeof(ipv6p->addr)); /* clear embedded scope in the address */ @@ -4406,7 +4406,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, * we can try their selection but it may not be * bound. */ - bzero(&lsa6_tmp, sizeof(lsa6_tmp)); + memset(&lsa6_tmp, 0, sizeof(lsa6_tmp)); lsa6_tmp.sin6_family = AF_INET6; lsa6_tmp.sin6_len = sizeof(lsa6_tmp); lsa6 = &lsa6_tmp; @@ -4491,7 +4491,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, * XXX: sa6 may not have a valid sin6_scope_id in * the non-SCOPEDROUTING case. */ - bzero(&lsa6_storage, sizeof(lsa6_storage)); + memset(&lsa6_storage, 0, sizeof(lsa6_storage)); lsa6_storage.sin6_family = AF_INET6; lsa6_storage.sin6_len = sizeof(lsa6_storage); lsa6_storage.sin6_addr = lsa6->sin6_addr; @@ -5143,7 +5143,10 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt, s.param_length = htons(sizeof(s) + plen); m_copyback(op_err, err_at, sizeof(s), (caddr_t)&s); err_at += sizeof(s); - phdr = sctp_get_next_param(mat, at, (struct sctp_paramhdr *)tempbuf, min(sizeof(tempbuf), plen)); + if (plen > sizeof(tempbuf)) { + plen = sizeof(tempbuf); + } + phdr = sctp_get_next_param(mat, at, (struct sctp_paramhdr *)tempbuf, plen); if (phdr == NULL) { sctp_m_freem(op_err); /* @@ -5211,7 +5214,7 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt, if (plen > sizeof(tempbuf)) { plen = sizeof(tempbuf); } - phdr = sctp_get_next_param(mat, at, (struct sctp_paramhdr *)tempbuf, min(sizeof(tempbuf), plen)); + phdr = sctp_get_next_param(mat, at, (struct sctp_paramhdr *)tempbuf, plen); if (phdr == NULL) { sctp_m_freem(op_err); /* @@ -5392,10 +5395,12 @@ sctp_are_there_new_addresses(struct sctp_association *asoc, { struct sctp_ipv4addr_param *p4, p4_buf; + if (plen != sizeof(struct sctp_ipv4addr_param)) { + return (1); + } phdr = sctp_get_next_param(in_initpkt, offset, (struct sctp_paramhdr *)&p4_buf, sizeof(p4_buf)); - if (plen != sizeof(struct sctp_ipv4addr_param) || - phdr == NULL) { + if (phdr == NULL) { return (1); } if (asoc->scope.ipv4_addr_legal) { @@ -5411,10 +5416,12 @@ sctp_are_there_new_addresses(struct sctp_association *asoc, { struct sctp_ipv6addr_param *p6, p6_buf; + if (plen != sizeof(struct sctp_ipv6addr_param)) { + return (1); + } phdr = sctp_get_next_param(in_initpkt, offset, (struct sctp_paramhdr *)&p6_buf, sizeof(p6_buf)); - if (plen != sizeof(struct sctp_ipv6addr_param) || - phdr == NULL) { + if (phdr == NULL) { return (1); } if (asoc->scope.ipv6_addr_legal) { @@ -6245,11 +6252,7 @@ sctp_get_frag_point(struct sctp_tcb *stcb, } else { ovh = SCTP_MIN_V4_OVERHEAD; } - if (stcb->asoc.idata_supported) { - ovh += sizeof(struct sctp_idata_chunk); - } else { - ovh += sizeof(struct sctp_data_chunk); - } + ovh += SCTP_DATA_CHUNK_OVERHEAD(stcb); if (stcb->asoc.sctp_frag_point > asoc->smallest_mtu) siz = asoc->smallest_mtu - ovh; else @@ -6754,7 +6757,7 @@ sctp_sendall_iterator(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, } } un_sent = ((stcb->asoc.total_output_queue_size - stcb->asoc.total_flight) + - (stcb->asoc.stream_queue_cnt * sizeof(struct sctp_data_chunk))); + (stcb->asoc.stream_queue_cnt * SCTP_DATA_CHUNK_OVERHEAD(stcb))); if ((sctp_is_feature_off(inp, SCTP_PCB_FLAGS_NODELAY)) && (stcb->asoc.total_flight > 0) && @@ -7454,11 +7457,7 @@ dont_do_it: } else { atomic_subtract_int(&sp->length, to_move); } - if (stcb->asoc.idata_supported == 0) { - leading = sizeof(struct sctp_data_chunk); - } else { - leading = sizeof(struct sctp_idata_chunk); - } + leading = SCTP_DATA_CHUNK_OVERHEAD(stcb); if (M_LEADINGSPACE(chk->data) < leading) { /* Not enough room for a chunk header, get some */ struct mbuf *m; @@ -7500,11 +7499,7 @@ dont_do_it: M_ALIGN(chk->data, 4); } } - if (stcb->asoc.idata_supported == 0) { - SCTP_BUF_PREPEND(chk->data, sizeof(struct sctp_data_chunk), M_NOWAIT); - } else { - SCTP_BUF_PREPEND(chk->data, sizeof(struct sctp_idata_chunk), M_NOWAIT); - } + SCTP_BUF_PREPEND(chk->data, SCTP_DATA_CHUNK_OVERHEAD(stcb), M_NOWAIT); if (chk->data == NULL) { /* HELP, TSNH since we assured it would not above? */ #ifdef INVARIANTS @@ -7517,13 +7512,8 @@ dont_do_it: to_move = 0; goto out_of; } - if (stcb->asoc.idata_supported == 0) { - sctp_snd_sb_alloc(stcb, sizeof(struct sctp_data_chunk)); - chk->book_size = chk->send_size = (uint16_t)(to_move + sizeof(struct sctp_data_chunk)); - } else { - sctp_snd_sb_alloc(stcb, sizeof(struct sctp_idata_chunk)); - chk->book_size = chk->send_size = (uint16_t)(to_move + sizeof(struct sctp_idata_chunk)); - } + sctp_snd_sb_alloc(stcb, SCTP_DATA_CHUNK_OVERHEAD(stcb)); + chk->book_size = chk->send_size = (uint16_t)(to_move + SCTP_DATA_CHUNK_OVERHEAD(stcb)); chk->book_size_scale = 0; chk->sent = SCTP_DATAGRAM_UNSENT; @@ -7723,11 +7713,7 @@ sctp_fill_outqueue(struct sctp_tcb *stcb, break; } /* Need an allowance for the data chunk header too */ - if (stcb->asoc.idata_supported == 0) { - space_left -= sizeof(struct sctp_data_chunk); - } else { - space_left -= sizeof(struct sctp_idata_chunk); - } + space_left -= SCTP_DATA_CHUNK_OVERHEAD(stcb); /* must make even word boundary */ space_left &= 0xfffffffc; @@ -7744,18 +7730,10 @@ sctp_fill_outqueue(struct sctp_tcb *stcb, strq = stcb->asoc.ss_functions.sctp_ss_select_stream(stcb, net, asoc); total_moved += moved; space_left -= moved; - if (stcb->asoc.idata_supported == 0) { - if (space_left >= sizeof(struct sctp_data_chunk)) { - space_left -= sizeof(struct sctp_data_chunk); - } else { - space_left = 0; - } + if (space_left >= SCTP_DATA_CHUNK_OVERHEAD(stcb)) { + space_left -= SCTP_DATA_CHUNK_OVERHEAD(stcb); } else { - if (space_left >= sizeof(struct sctp_idata_chunk)) { - space_left -= sizeof(struct sctp_idata_chunk); - } else { - space_left = 0; - } + space_left = 0; } space_left &= 0xfffffffc; } @@ -9002,7 +8980,7 @@ sctp_send_cookie_echo(struct mbuf *m, */ int at; struct mbuf *cookie; - struct sctp_paramhdr parm, *phdr; + struct sctp_paramhdr param, *phdr; struct sctp_chunkhdr *hdr; struct sctp_tmit_chunk *chk; uint16_t ptype, plen; @@ -9012,7 +8990,7 @@ sctp_send_cookie_echo(struct mbuf *m, cookie = NULL; at = offset + sizeof(struct sctp_init_chunk); for (;;) { - phdr = sctp_get_next_param(m, at, &parm, sizeof(parm)); + phdr = sctp_get_next_param(m, at, ¶m, sizeof(param)); if (phdr == NULL) { return (-3); } @@ -10204,8 +10182,7 @@ do_it_again: * and we have data in flight we stop, except if we * are handling a fragmented user message. */ - un_sent = ((stcb->asoc.total_output_queue_size - stcb->asoc.total_flight) + - (stcb->asoc.stream_queue_cnt * sizeof(struct sctp_data_chunk))); + un_sent = stcb->asoc.total_output_queue_size - stcb->asoc.total_flight; if ((un_sent < (int)(stcb->asoc.smallest_mtu - SCTP_MIN_OVERHEAD)) && (stcb->asoc.total_flight > 0)) { /* && sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXPLICIT_EOR))) {*/ @@ -12443,7 +12420,7 @@ sctp_copy_it_in(struct sctp_tcb *stcb, sp->sender_all_done = 0; sp->some_taken = 0; sp->put_last_out = 0; - resv_in_first = sizeof(struct sctp_data_chunk); + resv_in_first = SCTP_DATA_CHUNK_OVERHEAD(stcb); sp->data = sp->tail_mbuf = NULL; if (sp->length == 0) { *error = 0; @@ -12860,12 +12837,19 @@ sctp_lower_sosend(struct socket *so, } /* would we block? */ if (non_blocking) { + uint32_t amount; + if (hold_tcblock == 0) { SCTP_TCB_LOCK(stcb); hold_tcblock = 1; } - inqueue_bytes = stcb->asoc.total_output_queue_size - (stcb->asoc.chunks_on_out_queue * sizeof(struct sctp_data_chunk)); - if ((SCTP_SB_LIMIT_SND(so) < (sndlen + inqueue_bytes + stcb->asoc.sb_send_resv)) || + inqueue_bytes = stcb->asoc.total_output_queue_size - (stcb->asoc.chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb)); + if (user_marks_eor == 0) { + amount = sndlen; + } else { + amount = 1; + } + if ((SCTP_SB_LIMIT_SND(so) < (amount + inqueue_bytes + stcb->asoc.sb_send_resv)) || (stcb->asoc.chunks_on_out_queue >= SCTP_BASE_SYSCTL(sctp_max_chunks_on_queue))) { SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EWOULDBLOCK); if (sndlen > SCTP_SB_LIMIT_SND(so)) @@ -13033,7 +13017,7 @@ sctp_lower_sosend(struct socket *so, goto out_unlocked; } /* Calculate the maximum we can send */ - inqueue_bytes = stcb->asoc.total_output_queue_size - (stcb->asoc.chunks_on_out_queue * sizeof(struct sctp_data_chunk)); + inqueue_bytes = stcb->asoc.total_output_queue_size - (stcb->asoc.chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb)); if (SCTP_SB_LIMIT_SND(so) > inqueue_bytes) { if (non_blocking) { /* we already checked for non-blocking above. */ @@ -13090,7 +13074,7 @@ sctp_lower_sosend(struct socket *so, ((stcb->asoc.chunks_on_out_queue + stcb->asoc.stream_queue_cnt) >= SCTP_BASE_SYSCTL(sctp_max_chunks_on_queue))) { /* No room right now ! */ SOCKBUF_LOCK(&so->so_snd); - inqueue_bytes = stcb->asoc.total_output_queue_size - (stcb->asoc.chunks_on_out_queue * sizeof(struct sctp_data_chunk)); + inqueue_bytes = stcb->asoc.total_output_queue_size - (stcb->asoc.chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb)); while ((SCTP_SB_LIMIT_SND(so) < (inqueue_bytes + local_add_more)) || ((stcb->asoc.stream_queue_cnt + stcb->asoc.chunks_on_out_queue) >= SCTP_BASE_SYSCTL(sctp_max_chunks_on_queue))) { SCTPDBG(SCTP_DEBUG_OUTPUT1, "pre_block limit:%u <(inq:%d + %d) || (%d+%d > %d)\n", @@ -13126,7 +13110,7 @@ sctp_lower_sosend(struct socket *so, SOCKBUF_UNLOCK(&so->so_snd); goto out_unlocked; } - inqueue_bytes = stcb->asoc.total_output_queue_size - (stcb->asoc.chunks_on_out_queue * sizeof(struct sctp_data_chunk)); + inqueue_bytes = stcb->asoc.total_output_queue_size - (stcb->asoc.chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb)); } if (SCTP_SB_LIMIT_SND(so) > inqueue_bytes) { max_len = SCTP_SB_LIMIT_SND(so) - inqueue_bytes; @@ -13219,8 +13203,9 @@ skip_preblock: /* How much room do we have? */ struct mbuf *new_tail, *mm; - if (SCTP_SB_LIMIT_SND(so) > stcb->asoc.total_output_queue_size) - max_len = SCTP_SB_LIMIT_SND(so) - stcb->asoc.total_output_queue_size; + inqueue_bytes = stcb->asoc.total_output_queue_size - (stcb->asoc.chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb)); + if (SCTP_SB_LIMIT_SND(so) > inqueue_bytes) + max_len = SCTP_SB_LIMIT_SND(so) - inqueue_bytes; else max_len = 0; @@ -13296,8 +13281,8 @@ skip_preblock: hold_tcblock = 1; } sctp_prune_prsctp(stcb, asoc, srcv, sndlen); - inqueue_bytes = stcb->asoc.total_output_queue_size - (stcb->asoc.chunks_on_out_queue * sizeof(struct sctp_data_chunk)); - if (SCTP_SB_LIMIT_SND(so) > stcb->asoc.total_output_queue_size) + inqueue_bytes = stcb->asoc.total_output_queue_size - (stcb->asoc.chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb)); + if (SCTP_SB_LIMIT_SND(so) > inqueue_bytes) max_len = SCTP_SB_LIMIT_SND(so) - inqueue_bytes; else max_len = 0; @@ -13338,8 +13323,7 @@ skip_preblock: } asoc->ifp_had_enobuf = 0; } - un_sent = ((stcb->asoc.total_output_queue_size - stcb->asoc.total_flight) + - (stcb->asoc.stream_queue_cnt * sizeof(struct sctp_data_chunk))); + un_sent = stcb->asoc.total_output_queue_size - stcb->asoc.total_flight; if ((sctp_is_feature_off(inp, SCTP_PCB_FLAGS_NODELAY)) && (stcb->asoc.total_flight > 0) && (stcb->asoc.stream_queue_cnt < SCTP_MAX_DATA_BUNDLING) && @@ -13413,7 +13397,8 @@ skip_preblock: * size we KNOW we will get to sleep safely with the * wakeup flag in place. */ - if (SCTP_SB_LIMIT_SND(so) <= (stcb->asoc.total_output_queue_size + + inqueue_bytes = stcb->asoc.total_output_queue_size - (stcb->asoc.chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb)); + if (SCTP_SB_LIMIT_SND(so) <= (inqueue_bytes + min(SCTP_BASE_SYSCTL(sctp_add_more_threshold), SCTP_SB_LIMIT_SND(so)))) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_BLK_LOGGING_ENABLE) { sctp_log_block(SCTP_BLOCK_LOG_INTO_BLK, @@ -13610,8 +13595,7 @@ skip_out_eof: } asoc->ifp_had_enobuf = 0; } - un_sent = ((stcb->asoc.total_output_queue_size - stcb->asoc.total_flight) + - (stcb->asoc.stream_queue_cnt * sizeof(struct sctp_data_chunk))); + un_sent = stcb->asoc.total_output_queue_size - stcb->asoc.total_flight; if ((sctp_is_feature_off(inp, SCTP_PCB_FLAGS_NODELAY)) && (stcb->asoc.total_flight > 0) && (stcb->asoc.stream_queue_cnt < SCTP_MAX_DATA_BUNDLING) && @@ -13750,7 +13734,7 @@ sctp_add_auth_chunk(struct mbuf *m, struct mbuf **m_end, SCTP_BUF_RESV_UF(m_auth, SCTP_MIN_OVERHEAD); /* fill in the AUTH chunk details */ auth = mtod(m_auth, struct sctp_auth_chunk *); - bzero(auth, sizeof(*auth)); + memset(auth, 0, sizeof(*auth)); auth->ch.chunk_type = SCTP_AUTHENTICATION; auth->ch.chunk_flags = 0; chunk_len = sizeof(*auth) + diff --git a/freebsd/sys/netinet/sctp_output.h b/freebsd/sys/netinet/sctp_output.h index cd90b7d7..32f968c1 100644 --- a/freebsd/sys/netinet/sctp_output.h +++ b/freebsd/sys/netinet/sctp_output.h @@ -135,6 +135,11 @@ void sctp_fix_ecn_echo(struct sctp_association *); void sctp_move_chunks_from_net(struct sctp_tcb *stcb, struct sctp_nets *net); + +#define SCTP_DATA_CHUNK_OVERHEAD(stcb) ((stcb)->asoc.idata_supported ? \ + sizeof(struct sctp_idata_chunk) : \ + sizeof(struct sctp_data_chunk)) + int sctp_output(struct sctp_inpcb *, struct mbuf *, struct sockaddr *, struct mbuf *, struct thread *, int); diff --git a/freebsd/sys/netinet/sctp_pcb.c b/freebsd/sys/netinet/sctp_pcb.c index e32e63f4..1907991e 100644 --- a/freebsd/sys/netinet/sctp_pcb.c +++ b/freebsd/sys/netinet/sctp_pcb.c @@ -704,7 +704,7 @@ sctp_add_addr_to_vrf(uint32_t vrf_id, void *ifn, uint32_t ifn_index, return (NULL); } SCTP_INCR_LADDR_COUNT(); - bzero(wi, sizeof(*wi)); + memset(wi, 0, sizeof(*wi)); (void)SCTP_GETTIME_TIMEVAL(&wi->start_time); wi->ifa = sctp_ifap; wi->action = SCTP_ADD_IP_ADDRESS; @@ -813,7 +813,7 @@ out_now: return; } SCTP_INCR_LADDR_COUNT(); - bzero(wi, sizeof(*wi)); + memset(wi, 0, sizeof(*wi)); (void)SCTP_GETTIME_TIMEVAL(&wi->start_time); wi->ifa = sctp_ifap; wi->action = SCTP_DEL_IP_ADDRESS; @@ -2048,7 +2048,7 @@ sctp_findassociation_special_addr(struct mbuf *m, int offset, struct sctphdr *sh, struct sctp_inpcb **inp_p, struct sctp_nets **netp, struct sockaddr *dst) { - struct sctp_paramhdr *phdr, parm_buf; + struct sctp_paramhdr *phdr, param_buf; #if defined(INET) || defined(INET6) struct sctp_tcb *stcb; uint16_t ptype; @@ -2076,7 +2076,7 @@ sctp_findassociation_special_addr(struct mbuf *m, int offset, offset += sizeof(struct sctp_init_chunk); - phdr = sctp_get_next_param(m, offset, &parm_buf, sizeof(parm_buf)); + phdr = sctp_get_next_param(m, offset, ¶m_buf, sizeof(param_buf)); while (phdr != NULL) { /* now we must see if we want the parameter */ #if defined(INET) || defined(INET6) @@ -2090,10 +2090,10 @@ sctp_findassociation_special_addr(struct mbuf *m, int offset, if (ptype == SCTP_IPV4_ADDRESS && plen == sizeof(struct sctp_ipv4addr_param)) { /* Get the rest of the address */ - struct sctp_ipv4addr_param ip4_parm, *p4; + struct sctp_ipv4addr_param ip4_param, *p4; phdr = sctp_get_next_param(m, offset, - (struct sctp_paramhdr *)&ip4_parm, min(plen, sizeof(ip4_parm))); + (struct sctp_paramhdr *)&ip4_param, sizeof(ip4_param)); if (phdr == NULL) { return (NULL); } @@ -2111,10 +2111,10 @@ sctp_findassociation_special_addr(struct mbuf *m, int offset, if (ptype == SCTP_IPV6_ADDRESS && plen == sizeof(struct sctp_ipv6addr_param)) { /* Get the rest of the address */ - struct sctp_ipv6addr_param ip6_parm, *p6; + struct sctp_ipv6addr_param ip6_param, *p6; phdr = sctp_get_next_param(m, offset, - (struct sctp_paramhdr *)&ip6_parm, min(plen, sizeof(ip6_parm))); + (struct sctp_paramhdr *)&ip6_param, sizeof(ip6_param)); if (phdr == NULL) { return (NULL); } @@ -2129,8 +2129,8 @@ sctp_findassociation_special_addr(struct mbuf *m, int offset, } #endif offset += SCTP_SIZE32(plen); - phdr = sctp_get_next_param(m, offset, &parm_buf, - sizeof(parm_buf)); + phdr = sctp_get_next_param(m, offset, ¶m_buf, + sizeof(param_buf)); } return (NULL); } @@ -2303,7 +2303,7 @@ sctp_findassociation_ep_asconf(struct mbuf *m, int offset, { struct sctp_tcb *stcb; union sctp_sockstore remote_store; - struct sctp_paramhdr parm_buf, *phdr; + struct sctp_paramhdr param_buf, *phdr; int ptype; int zero_address = 0; #ifdef INET @@ -2315,7 +2315,7 @@ sctp_findassociation_ep_asconf(struct mbuf *m, int offset, memset(&remote_store, 0, sizeof(remote_store)); phdr = sctp_get_next_param(m, offset + sizeof(struct sctp_asconf_chunk), - &parm_buf, sizeof(struct sctp_paramhdr)); + ¶m_buf, sizeof(struct sctp_paramhdr)); if (phdr == NULL) { SCTPDBG(SCTP_DEBUG_INPUT3, "%s: failed to get asconf lookup addr\n", __func__); @@ -2335,7 +2335,7 @@ sctp_findassociation_ep_asconf(struct mbuf *m, int offset, } p6 = (struct sctp_ipv6addr_param *)sctp_get_next_param(m, offset + sizeof(struct sctp_asconf_chunk), - &p6_buf.ph, sizeof(*p6)); + &p6_buf.ph, sizeof(p6_buf)); if (p6 == NULL) { SCTPDBG(SCTP_DEBUG_INPUT3, "%s: failed to get asconf v6 lookup addr\n", __func__); @@ -2362,7 +2362,7 @@ sctp_findassociation_ep_asconf(struct mbuf *m, int offset, } p4 = (struct sctp_ipv4addr_param *)sctp_get_next_param(m, offset + sizeof(struct sctp_asconf_chunk), - &p4_buf.ph, sizeof(*p4)); + &p4_buf.ph, sizeof(p4_buf)); if (p4 == NULL) { SCTPDBG(SCTP_DEBUG_INPUT3, "%s: failed to get asconf v4 lookup addr\n", __func__); @@ -2431,7 +2431,7 @@ sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id) return (ENOBUFS); } /* zap it */ - bzero(inp, sizeof(*inp)); + memset(inp, 0, sizeof(*inp)); /* bump generations */ /* setup socket pointers */ @@ -2717,7 +2717,7 @@ sctp_move_pcb_and_assoc(struct sctp_inpcb *old_inp, struct sctp_inpcb *new_inp, continue; } SCTP_INCR_LADDR_COUNT(); - bzero(laddr, sizeof(*laddr)); + memset(laddr, 0, sizeof(*laddr)); (void)SCTP_GETTIME_TIMEVAL(&laddr->start_time); laddr->ifa = oladdr->ifa; atomic_add_int(&laddr->ifa->refcount, 1); @@ -2767,7 +2767,7 @@ sctp_insert_laddr(struct sctpladdr *list, struct sctp_ifa *ifa, uint32_t act) return (EINVAL); } SCTP_INCR_LADDR_COUNT(); - bzero(laddr, sizeof(*laddr)); + memset(laddr, 0, sizeof(*laddr)); (void)SCTP_GETTIME_TIMEVAL(&laddr->start_time); laddr->ifa = ifa; laddr->action = act; @@ -3770,7 +3770,7 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, /* Invalid address */ return (-1); } - /* zero out the bzero area */ + /* zero out the zero area */ memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); /* assure len is set */ @@ -3853,7 +3853,7 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, return (-1); } SCTP_INCR_RADDR_COUNT(); - bzero(net, sizeof(struct sctp_nets)); + memset(net, 0, sizeof(struct sctp_nets)); (void)SCTP_GETTIME_TIMEVAL(&net->start_time); memcpy(&net->ro._l_addr, newaddr, newaddr->sa_len); switch (newaddr->sa_family) { @@ -4289,7 +4289,7 @@ sctp_aloc_assoc(struct sctp_inpcb *inp, struct sockaddr *firstaddr, } SCTP_INCR_ASOC_COUNT(); - bzero(stcb, sizeof(*stcb)); + memset(stcb, 0, sizeof(*stcb)); asoc = &stcb->asoc; asoc->assoc_id = sctp_aloc_a_assoc_id(inp, stcb); @@ -4603,21 +4603,21 @@ void sctp_clean_up_stream(struct sctp_tcb *stcb, struct sctp_readhead *rh) { struct sctp_tmit_chunk *chk, *nchk; - struct sctp_queued_to_read *ctl, *nctl; + struct sctp_queued_to_read *control, *ncontrol; - TAILQ_FOREACH_SAFE(ctl, rh, next_instrm, nctl) { - TAILQ_REMOVE(rh, ctl, next_instrm); - ctl->on_strm_q = 0; - if (ctl->on_read_q == 0) { - sctp_free_remote_addr(ctl->whoFrom); - if (ctl->data) { - sctp_m_freem(ctl->data); - ctl->data = NULL; + TAILQ_FOREACH_SAFE(control, rh, next_instrm, ncontrol) { + TAILQ_REMOVE(rh, control, next_instrm); + control->on_strm_q = 0; + if (control->on_read_q == 0) { + sctp_free_remote_addr(control->whoFrom); + if (control->data) { + sctp_m_freem(control->data); + control->data = NULL; } } /* Reassembly free? */ - TAILQ_FOREACH_SAFE(chk, &ctl->reasm, sctp_next, nchk) { - TAILQ_REMOVE(&ctl->reasm, chk, sctp_next); + TAILQ_FOREACH_SAFE(chk, &control->reasm, sctp_next, nchk) { + TAILQ_REMOVE(&control->reasm, chk, sctp_next); if (chk->data) { sctp_m_freem(chk->data); chk->data = NULL; @@ -4633,8 +4633,8 @@ sctp_clean_up_stream(struct sctp_tcb *stcb, struct sctp_readhead *rh) * We don't free the address here since all the net's were * freed above. */ - if (ctl->on_read_q == 0) { - sctp_free_a_readq(stcb, ctl); + if (control->on_read_q == 0) { + sctp_free_a_readq(stcb, control); } } } @@ -4860,7 +4860,7 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre inp->sctp_flags &= ~SCTP_PCB_FLAGS_CONNECTED; inp->sctp_flags |= SCTP_PCB_FLAGS_WAS_CONNECTED; if (so) { - SOCK_LOCK(so); + SOCKBUF_LOCK(&so->so_rcv); if (so->so_rcv.sb_cc == 0) { so->so_state &= ~(SS_ISCONNECTING | SS_ISDISCONNECTING | @@ -5735,7 +5735,7 @@ sctp_pcb_init() SCTP_BASE_VAR(sctp_pcb_initialized) = 1; #if defined(SCTP_LOCAL_TRACE_BUF) - bzero(&SCTP_BASE_SYSCTL(sctp_log), sizeof(struct sctp_log)); + memset(&SCTP_BASE_SYSCTL(sctp_log), 0, sizeof(struct sctp_log)); #endif #if defined(__FreeBSD__) && defined(SMP) && defined(SCTP_USE_PERCPU_STAT) SCTP_MALLOC(SCTP_BASE_STATS, struct sctpstat *, @@ -5744,11 +5744,11 @@ sctp_pcb_init() #endif (void)SCTP_GETTIME_TIMEVAL(&tv); #if defined(__FreeBSD__) && defined(SMP) && defined(SCTP_USE_PERCPU_STAT) - bzero(SCTP_BASE_STATS, (sizeof(struct sctpstat) * (mp_maxid + 1))); + memset(SCTP_BASE_STATS, 0, sizeof(struct sctpstat) * (mp_maxid + 1)); SCTP_BASE_STATS[PCPU_GET(cpuid)].sctps_discontinuitytime.tv_sec = (uint32_t)tv.tv_sec; SCTP_BASE_STATS[PCPU_GET(cpuid)].sctps_discontinuitytime.tv_usec = (uint32_t)tv.tv_usec; #else - bzero(&SCTP_BASE_STATS, sizeof(struct sctpstat)); + memset(&SCTP_BASE_STATS, 0, sizeof(struct sctpstat)); SCTP_BASE_STAT(sctps_discontinuitytime).tv_sec = (uint32_t)tv.tv_sec; SCTP_BASE_STAT(sctps_discontinuitytime).tv_usec = (uint32_t)tv.tv_usec; #endif @@ -6030,7 +6030,7 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m, */ struct sctp_inpcb *inp; struct sctp_nets *net, *nnet, *net_tmp; - struct sctp_paramhdr *phdr, parm_buf; + struct sctp_paramhdr *phdr, param_buf; struct sctp_tcb *stcb_tmp; uint16_t ptype, plen; struct sockaddr *sa; @@ -6140,7 +6140,7 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m, return (-4); } /* now we must go through each of the params. */ - phdr = sctp_get_next_param(m, offset, &parm_buf, sizeof(parm_buf)); + phdr = sctp_get_next_param(m, offset, ¶m_buf, sizeof(param_buf)); while (phdr) { ptype = ntohs(phdr->param_type); plen = ntohs(phdr->param_length); @@ -6378,7 +6378,7 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m, } phdr = sctp_get_next_param(m, offset, (struct sctp_paramhdr *)&lstore, - min(plen, sizeof(lstore))); + plen); if (phdr == NULL) { return (-24); } @@ -6431,8 +6431,11 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m, uint8_t local_store[SCTP_PARAM_BUFFER_SIZE]; int num_ent, i; + if (plen > sizeof(local_store)) { + return (-35); + } phdr = sctp_get_next_param(m, offset, - (struct sctp_paramhdr *)&local_store, min(sizeof(local_store), plen)); + (struct sctp_paramhdr *)&local_store, plen); if (phdr == NULL) { return (-25); } @@ -6479,7 +6482,7 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m, } phdr = sctp_get_next_param(m, offset, (struct sctp_paramhdr *)random_store, - min(sizeof(random_store), plen)); + plen); if (phdr == NULL) return (-26); p_random = (struct sctp_auth_random *)phdr; @@ -6502,7 +6505,7 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m, } phdr = sctp_get_next_param(m, offset, (struct sctp_paramhdr *)hmacs_store, - min(plen, sizeof(hmacs_store))); + plen); if (phdr == NULL) return (-28); hmacs = (struct sctp_auth_hmac_algo *)phdr; @@ -6533,7 +6536,7 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m, } phdr = sctp_get_next_param(m, offset, (struct sctp_paramhdr *)chunks_store, - min(plen, sizeof(chunks_store))); + plen); if (phdr == NULL) return (-30); chunks = (struct sctp_auth_chunk_list *)phdr; @@ -6581,8 +6584,8 @@ next_param: if (offset >= limit) { break; } - phdr = sctp_get_next_param(m, offset, &parm_buf, - sizeof(parm_buf)); + phdr = sctp_get_next_param(m, offset, ¶m_buf, + sizeof(param_buf)); } /* Now check to see if we need to purge any addresses */ TAILQ_FOREACH_SAFE(net, &stcb->asoc.nets, sctp_next, nnet) { @@ -6657,17 +6660,17 @@ next_param: /* copy in the RANDOM */ if (p_random != NULL) { keylen = sizeof(*p_random) + random_len; - bcopy(p_random, new_key->key, keylen); + memcpy(new_key->key, p_random, keylen); } /* append in the AUTH chunks */ if (chunks != NULL) { - bcopy(chunks, new_key->key + keylen, + memcpy(new_key->key + keylen, chunks, sizeof(*chunks) + num_chunks); keylen += sizeof(*chunks) + num_chunks; } /* append in the HMACs */ if (hmacs != NULL) { - bcopy(hmacs, new_key->key + keylen, + memcpy(new_key->key + keylen, hmacs, sizeof(*hmacs) + hmacs_len); } } else { @@ -6802,7 +6805,7 @@ sctp_drain_mbufs(struct sctp_tcb *stcb) struct sctp_association *asoc; struct sctp_tmit_chunk *chk, *nchk; uint32_t cumulative_tsn_p1; - struct sctp_queued_to_read *ctl, *nctl; + struct sctp_queued_to_read *control, *ncontrol; int cnt, strmat; uint32_t gap, i; int fnd = 0; @@ -6819,88 +6822,124 @@ sctp_drain_mbufs(struct sctp_tcb *stcb) cnt = 0; /* Ok that was fun, now we will drain all the inbound streams? */ for (strmat = 0; strmat < asoc->streamincnt; strmat++) { - TAILQ_FOREACH_SAFE(ctl, &asoc->strmin[strmat].inqueue, next_instrm, nctl) { + TAILQ_FOREACH_SAFE(control, &asoc->strmin[strmat].inqueue, next_instrm, ncontrol) { #ifdef INVARIANTS - if (ctl->on_strm_q != SCTP_ON_ORDERED) { + if (control->on_strm_q != SCTP_ON_ORDERED) { panic("Huh control: %p on_q: %d -- not ordered?", - ctl, ctl->on_strm_q); + control, control->on_strm_q); } #endif - if (SCTP_TSN_GT(ctl->sinfo_tsn, cumulative_tsn_p1)) { + if (SCTP_TSN_GT(control->sinfo_tsn, cumulative_tsn_p1)) { /* Yep it is above cum-ack */ cnt++; - SCTP_CALC_TSN_TO_GAP(gap, ctl->sinfo_tsn, asoc->mapping_array_base_tsn); - asoc->size_on_all_streams = sctp_sbspace_sub(asoc->size_on_all_streams, ctl->length); + SCTP_CALC_TSN_TO_GAP(gap, control->sinfo_tsn, asoc->mapping_array_base_tsn); + KASSERT(control->length > 0, ("control has zero length")); + if (asoc->size_on_all_streams >= control->length) { + asoc->size_on_all_streams -= control->length; + } else { +#ifdef INVARIANTS + panic("size_on_all_streams = %u smaller than control length %u", asoc->size_on_all_streams, control->length); +#else + asoc->size_on_all_streams = 0; +#endif + } sctp_ucount_decr(asoc->cnt_on_all_streams); SCTP_UNSET_TSN_PRESENT(asoc->mapping_array, gap); - if (ctl->on_read_q) { - TAILQ_REMOVE(&stcb->sctp_ep->read_queue, ctl, next); - ctl->on_read_q = 0; + if (control->on_read_q) { + TAILQ_REMOVE(&stcb->sctp_ep->read_queue, control, next); + control->on_read_q = 0; } - TAILQ_REMOVE(&asoc->strmin[strmat].inqueue, ctl, next_instrm); - ctl->on_strm_q = 0; - if (ctl->data) { - sctp_m_freem(ctl->data); - ctl->data = NULL; + TAILQ_REMOVE(&asoc->strmin[strmat].inqueue, control, next_instrm); + control->on_strm_q = 0; + if (control->data) { + sctp_m_freem(control->data); + control->data = NULL; } - sctp_free_remote_addr(ctl->whoFrom); + sctp_free_remote_addr(control->whoFrom); /* Now its reasm? */ - TAILQ_FOREACH_SAFE(chk, &ctl->reasm, sctp_next, nchk) { + TAILQ_FOREACH_SAFE(chk, &control->reasm, sctp_next, nchk) { cnt++; SCTP_CALC_TSN_TO_GAP(gap, chk->rec.data.tsn, asoc->mapping_array_base_tsn); - asoc->size_on_reasm_queue = sctp_sbspace_sub(asoc->size_on_reasm_queue, chk->send_size); + KASSERT(chk->send_size > 0, ("chunk has zero length")); + if (asoc->size_on_reasm_queue >= chk->send_size) { + asoc->size_on_reasm_queue -= chk->send_size; + } else { +#ifdef INVARIANTS + panic("size_on_reasm_queue = %u smaller than chunk length %u", asoc->size_on_reasm_queue, chk->send_size); +#else + asoc->size_on_reasm_queue = 0; +#endif + } sctp_ucount_decr(asoc->cnt_on_reasm_queue); SCTP_UNSET_TSN_PRESENT(asoc->mapping_array, gap); - TAILQ_REMOVE(&ctl->reasm, chk, sctp_next); + TAILQ_REMOVE(&control->reasm, chk, sctp_next); if (chk->data) { sctp_m_freem(chk->data); chk->data = NULL; } sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED); } - sctp_free_a_readq(stcb, ctl); + sctp_free_a_readq(stcb, control); } } - TAILQ_FOREACH_SAFE(ctl, &asoc->strmin[strmat].uno_inqueue, next_instrm, nctl) { + TAILQ_FOREACH_SAFE(control, &asoc->strmin[strmat].uno_inqueue, next_instrm, ncontrol) { #ifdef INVARIANTS - if (ctl->on_strm_q != SCTP_ON_UNORDERED) { + if (control->on_strm_q != SCTP_ON_UNORDERED) { panic("Huh control: %p on_q: %d -- not unordered?", - ctl, ctl->on_strm_q); + control, control->on_strm_q); } #endif - if (SCTP_TSN_GT(ctl->sinfo_tsn, cumulative_tsn_p1)) { + if (SCTP_TSN_GT(control->sinfo_tsn, cumulative_tsn_p1)) { /* Yep it is above cum-ack */ cnt++; - SCTP_CALC_TSN_TO_GAP(gap, ctl->sinfo_tsn, asoc->mapping_array_base_tsn); - asoc->size_on_all_streams = sctp_sbspace_sub(asoc->size_on_all_streams, ctl->length); + SCTP_CALC_TSN_TO_GAP(gap, control->sinfo_tsn, asoc->mapping_array_base_tsn); + KASSERT(control->length > 0, ("control has zero length")); + if (asoc->size_on_all_streams >= control->length) { + asoc->size_on_all_streams -= control->length; + } else { +#ifdef INVARIANTS + panic("size_on_all_streams = %u smaller than control length %u", asoc->size_on_all_streams, control->length); +#else + asoc->size_on_all_streams = 0; +#endif + } sctp_ucount_decr(asoc->cnt_on_all_streams); SCTP_UNSET_TSN_PRESENT(asoc->mapping_array, gap); - if (ctl->on_read_q) { - TAILQ_REMOVE(&stcb->sctp_ep->read_queue, ctl, next); - ctl->on_read_q = 0; + if (control->on_read_q) { + TAILQ_REMOVE(&stcb->sctp_ep->read_queue, control, next); + control->on_read_q = 0; } - TAILQ_REMOVE(&asoc->strmin[strmat].uno_inqueue, ctl, next_instrm); - ctl->on_strm_q = 0; - if (ctl->data) { - sctp_m_freem(ctl->data); - ctl->data = NULL; + TAILQ_REMOVE(&asoc->strmin[strmat].uno_inqueue, control, next_instrm); + control->on_strm_q = 0; + if (control->data) { + sctp_m_freem(control->data); + control->data = NULL; } - sctp_free_remote_addr(ctl->whoFrom); + sctp_free_remote_addr(control->whoFrom); /* Now its reasm? */ - TAILQ_FOREACH_SAFE(chk, &ctl->reasm, sctp_next, nchk) { + TAILQ_FOREACH_SAFE(chk, &control->reasm, sctp_next, nchk) { cnt++; SCTP_CALC_TSN_TO_GAP(gap, chk->rec.data.tsn, asoc->mapping_array_base_tsn); - asoc->size_on_reasm_queue = sctp_sbspace_sub(asoc->size_on_reasm_queue, chk->send_size); + KASSERT(chk->send_size > 0, ("chunk has zero length")); + if (asoc->size_on_reasm_queue >= chk->send_size) { + asoc->size_on_reasm_queue -= chk->send_size; + } else { +#ifdef INVARIANTS + panic("size_on_reasm_queue = %u smaller than chunk length %u", asoc->size_on_reasm_queue, chk->send_size); +#else + asoc->size_on_reasm_queue = 0; +#endif + } sctp_ucount_decr(asoc->cnt_on_reasm_queue); SCTP_UNSET_TSN_PRESENT(asoc->mapping_array, gap); - TAILQ_REMOVE(&ctl->reasm, chk, sctp_next); + TAILQ_REMOVE(&control->reasm, chk, sctp_next); if (chk->data) { sctp_m_freem(chk->data); chk->data = NULL; } sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED); } - sctp_free_a_readq(stcb, ctl); + sctp_free_a_readq(stcb, control); } } } diff --git a/freebsd/sys/netinet/sctp_sysctl.c b/freebsd/sys/netinet/sctp_sysctl.c index db150112..a2364431 100644 --- a/freebsd/sys/netinet/sctp_sysctl.c +++ b/freebsd/sys/netinet/sctp_sysctl.c @@ -417,12 +417,12 @@ sctp_sysctl_handle_assoclist(SYSCTL_HANDLER_ARGS) xinpcb.qlen = 0; xinpcb.maxqlen = 0; } else { - xinpcb.qlen = so->so_qlen; - xinpcb.qlen_old = so->so_qlen > USHRT_MAX ? - USHRT_MAX : (uint16_t)so->so_qlen; - xinpcb.maxqlen = so->so_qlimit; - xinpcb.maxqlen_old = so->so_qlimit > USHRT_MAX ? - USHRT_MAX : (uint16_t)so->so_qlimit; + xinpcb.qlen = so->sol_qlen; + xinpcb.qlen_old = so->sol_qlen > USHRT_MAX ? + USHRT_MAX : (uint16_t)so->sol_qlen; + xinpcb.maxqlen = so->sol_qlimit; + xinpcb.maxqlen_old = so->sol_qlimit > USHRT_MAX ? + USHRT_MAX : (uint16_t)so->sol_qlimit; } SCTP_INP_INCR_REF(inp); SCTP_INP_RUNLOCK(inp); diff --git a/freebsd/sys/netinet/sctp_usrreq.c b/freebsd/sys/netinet/sctp_usrreq.c index b65f74d1..fa136d4e 100644 --- a/freebsd/sys/netinet/sctp_usrreq.c +++ b/freebsd/sys/netinet/sctp_usrreq.c @@ -87,7 +87,7 @@ sctp_init(void) #if defined(SCTP_PACKET_LOGGING) SCTP_BASE_VAR(packet_log_writers) = 0; SCTP_BASE_VAR(packet_log_end) = 0; - bzero(&SCTP_BASE_VAR(packet_log_buffer), SCTP_PACKET_LOG_SIZE); + memset(&SCTP_BASE_VAR(packet_log_buffer), 0, SCTP_PACKET_LOG_SIZE); #endif } @@ -7140,19 +7140,16 @@ sctp_listen(struct socket *so, int backlog, struct thread *p) } } SCTP_INP_WLOCK(inp); - SOCK_LOCK(so); - /* It appears for 7.0 and on, we must always call this. */ - solisten_proto(so, backlog); - if (inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) { - /* remove the ACCEPTCONN flag for one-to-many sockets */ - so->so_options &= ~SO_ACCEPTCONN; + if ((inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) == 0) { + SOCK_LOCK(so); + solisten_proto(so, backlog); + SOCK_UNLOCK(so); } if (backlog > 0) { inp->sctp_flags |= SCTP_PCB_FLAGS_ACCEPTING; } else { inp->sctp_flags &= ~SCTP_PCB_FLAGS_ACCEPTING; } - SOCK_UNLOCK(so); SCTP_INP_WUNLOCK(inp); return (error); } diff --git a/freebsd/sys/netinet/sctputil.c b/freebsd/sys/netinet/sctputil.c index 4c6ba598..75c989d3 100644 --- a/freebsd/sys/netinet/sctputil.c +++ b/freebsd/sys/netinet/sctputil.c @@ -2595,7 +2595,7 @@ sctp_m_getptr(struct mbuf *m, int off, int len, uint8_t *in_ptr) /* else, it spans more than one mbuf, so save a temp copy... */ while ((m != NULL) && (len > 0)) { count = min(SCTP_BUF_LEN(m) - off, len); - bcopy(mtod(m, caddr_t)+off, ptr, count); + memcpy(ptr, mtod(m, caddr_t)+off, count); len -= count; ptr += count; off = 0; @@ -2756,9 +2756,9 @@ sctp_notify_assoc_change(uint16_t state, struct sctp_tcb *stcb, m_notify); if (control != NULL) { control->length = SCTP_BUF_LEN(m_notify); + control->spec_flags = M_NOTIFICATION; /* not that we need this */ control->tail_mbuf = m_notify; - control->spec_flags = M_NOTIFICATION; sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, 1, SCTP_READ_LOCK_NOT_HELD, @@ -2794,6 +2794,7 @@ set_error: stcb->sctp_socket->so_error = ECONNABORTED; } } + SOCK_UNLOCK(stcb->sctp_socket); } /* Wake ANY sleepers */ #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) @@ -2813,7 +2814,7 @@ set_error: if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) && ((state == SCTP_COMM_LOST) || (state == SCTP_CANT_STR_ASSOC))) { - socantrcvmore_locked(stcb->sctp_socket); + socantrcvmore(stcb->sctp_socket); } sorwakeup(stcb->sctp_socket); sowwakeup(stcb->sctp_socket); @@ -3040,7 +3041,10 @@ sctp_notify_send_failed(struct sctp_tcb *stcb, uint8_t sent, uint32_t error, sctp_m_freem(m_notify); return; } + control->length = SCTP_BUF_LEN(m_notify); control->spec_flags = M_NOTIFICATION; + /* not that we need this */ + control->tail_mbuf = m_notify; sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, 1, @@ -3140,7 +3144,10 @@ sctp_notify_send_failed2(struct sctp_tcb *stcb, uint32_t error, sctp_m_freem(m_notify); return; } + control->length = SCTP_BUF_LEN(m_notify); control->spec_flags = M_NOTIFICATION; + /* not that we need this */ + control->tail_mbuf = m_notify; sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, 1, SCTP_READ_LOCK_NOT_HELD, so_locked); @@ -3241,12 +3248,10 @@ sctp_notify_partial_delivery_indication(struct sctp_tcb *stcb, uint32_t error, sctp_m_freem(m_notify); return; } - control->spec_flags = M_NOTIFICATION; control->length = SCTP_BUF_LEN(m_notify); + control->spec_flags = M_NOTIFICATION; /* not that we need this */ control->tail_mbuf = m_notify; - control->held_length = 0; - control->length = 0; sb = &stcb->sctp_socket->so_rcv; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(sb, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBALLOC, SCTP_BUF_LEN(m_notify)); @@ -3255,7 +3260,6 @@ sctp_notify_partial_delivery_indication(struct sctp_tcb *stcb, uint32_t error, if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(sb, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBRESULT, 0); } - atomic_add_int(&control->length, SCTP_BUF_LEN(m_notify)); control->end_added = 1; if (stcb->asoc.control_pdapi) TAILQ_INSERT_AFTER(&stcb->sctp_ep->read_queue, stcb->asoc.control_pdapi, control, next); @@ -3350,8 +3354,8 @@ sctp_notify_shutdown_event(struct sctp_tcb *stcb) sctp_m_freem(m_notify); return; } - control->spec_flags = M_NOTIFICATION; control->length = SCTP_BUF_LEN(m_notify); + control->spec_flags = M_NOTIFICATION; /* not that we need this */ control->tail_mbuf = m_notify; sctp_add_to_readq(stcb->sctp_ep, stcb, @@ -3457,8 +3461,8 @@ sctp_notify_stream_reset_add(struct sctp_tcb *stcb, uint16_t numberin, uint16_t sctp_m_freem(m_notify); return; } - control->spec_flags = M_NOTIFICATION; control->length = SCTP_BUF_LEN(m_notify); + control->spec_flags = M_NOTIFICATION; /* not that we need this */ control->tail_mbuf = m_notify; sctp_add_to_readq(stcb->sctp_ep, stcb, @@ -3507,8 +3511,8 @@ sctp_notify_stream_reset_tsn(struct sctp_tcb *stcb, uint32_t sending_tsn, uint32 sctp_m_freem(m_notify); return; } - control->spec_flags = M_NOTIFICATION; control->length = SCTP_BUF_LEN(m_notify); + control->spec_flags = M_NOTIFICATION; /* not that we need this */ control->tail_mbuf = m_notify; sctp_add_to_readq(stcb->sctp_ep, stcb, @@ -3572,8 +3576,8 @@ sctp_notify_stream_reset(struct sctp_tcb *stcb, sctp_m_freem(m_notify); return; } - control->spec_flags = M_NOTIFICATION; control->length = SCTP_BUF_LEN(m_notify); + control->spec_flags = M_NOTIFICATION; /* not that we need this */ control->tail_mbuf = m_notify; sctp_add_to_readq(stcb->sctp_ep, stcb, @@ -3628,9 +3632,9 @@ sctp_notify_remote_error(struct sctp_tcb *stcb, uint16_t error, struct sctp_erro m_notify); if (control != NULL) { control->length = SCTP_BUF_LEN(m_notify); + control->spec_flags = M_NOTIFICATION; /* not that we need this */ control->tail_mbuf = m_notify; - control->spec_flags = M_NOTIFICATION; sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, 1, @@ -6165,7 +6169,7 @@ sctp_dynamic_set_primary(struct sockaddr *sa, uint32_t vrf_id) } /* Now incr the count and int wi structure */ SCTP_INCR_LADDR_COUNT(); - bzero(wi, sizeof(*wi)); + memset(wi, 0, sizeof(*wi)); (void)SCTP_GETTIME_TIMEVAL(&wi->start_time); wi->ifa = ifa; wi->action = SCTP_SET_PRIM_ADDR; @@ -7241,8 +7245,6 @@ sctp_over_udp_start(void) return (0); } -#if defined(INET6) || defined(INET) - /* * sctp_min_mtu ()returns the minimum of all non-zero arguments. * If all arguments are zero, zero is returned. @@ -7326,4 +7328,3 @@ sctp_hc_get_mtu(union sctp_sockstore *addr, uint16_t fibnum) } return ((uint32_t)tcp_hc_getmtu(&inc)); } -#endif diff --git a/freebsd/sys/netinet/sctputil.h b/freebsd/sys/netinet/sctputil.h index 50118b7a..97b33654 100644 --- a/freebsd/sys/netinet/sctputil.h +++ b/freebsd/sys/netinet/sctputil.h @@ -388,10 +388,8 @@ sctp_auditing(int, struct sctp_inpcb *, struct sctp_tcb *, void sctp_audit_log(uint8_t, uint8_t); #endif -#if defined(INET6) || defined(INET) uint32_t sctp_min_mtu(uint32_t, uint32_t, uint32_t); void sctp_hc_set_mtu(union sctp_sockstore *, uint16_t, uint32_t); uint32_t sctp_hc_get_mtu(union sctp_sockstore *, uint16_t); -#endif #endif /* _KERNEL */ #endif diff --git a/freebsd/sys/netinet/tcp_input.c b/freebsd/sys/netinet/tcp_input.c index d7091928..2195a778 100644 --- a/freebsd/sys/netinet/tcp_input.c +++ b/freebsd/sys/netinet/tcp_input.c @@ -437,16 +437,9 @@ cc_cong_signal(struct tcpcb *tp, struct tcphdr *th, uint32_t type) tp->t_dupacks = 0; tp->t_bytes_acked = 0; EXIT_RECOVERY(tp->t_flags); - if (CC_ALGO(tp)->cong_signal == NULL) { - /* - * RFC5681 Section 3.1 - * ssthresh = max (FlightSize / 2, 2*SMSS) eq (4) - */ - tp->snd_ssthresh = - max((tp->snd_max - tp->snd_una) / 2 / maxseg, 2) - * maxseg; - tp->snd_cwnd = maxseg; - } + tp->snd_ssthresh = max(2, min(tp->snd_wnd, tp->snd_cwnd) / 2 / + maxseg) * maxseg; + tp->snd_cwnd = maxseg; break; case CC_RTO_ERR: TCPSTAT_INC(tcps_sndrexmitbad); @@ -2013,8 +2006,10 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, /* * If the state is SYN_SENT: - * if seg contains a RST, then drop the connection. - * if seg does not contain SYN, then drop it. + * if seg contains a RST with valid ACK (SEQ.ACK has already + * been verified), then drop the connection. + * if seg contains a RST without an ACK, drop the seg. + * if seg does not contain SYN, then drop the seg. * Otherwise this is an acceptable SYN segment * initialize tp->rcv_nxt and tp->irs * if seg contains ack then advance tp->snd_una @@ -2613,15 +2608,6 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, if (awnd < tp->snd_ssthresh) { tp->snd_cwnd += maxseg; - /* - * RFC5681 Section 3.2 talks about cwnd - * inflation on additional dupacks and - * deflation on recovering from loss. - * - * We keep cwnd into check so that - * we don't have to 'deflate' it when we - * get out of recovery. - */ if (tp->snd_cwnd > tp->snd_ssthresh) tp->snd_cwnd = tp->snd_ssthresh; } @@ -2661,22 +2647,19 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, TCPSTAT_INC( tcps_sack_recovery_episode); tp->sack_newdata = tp->snd_nxt; - if (CC_ALGO(tp)->cong_signal == NULL) - tp->snd_cwnd = maxseg; + tp->snd_cwnd = maxseg; (void) tp->t_fb->tfb_tcp_output(tp); goto drop; } tp->snd_nxt = th->th_ack; - if (CC_ALGO(tp)->cong_signal == NULL) - tp->snd_cwnd = maxseg; + tp->snd_cwnd = maxseg; (void) tp->t_fb->tfb_tcp_output(tp); KASSERT(tp->snd_limited <= 2, ("%s: tp->snd_limited too big", __func__)); - if (CC_ALGO(tp)->cong_signal == NULL) - tp->snd_cwnd = tp->snd_ssthresh + - maxseg * - (tp->t_dupacks - tp->snd_limited); + tp->snd_cwnd = tp->snd_ssthresh + + maxseg * + (tp->t_dupacks - tp->snd_limited); if (SEQ_GT(onxt, tp->snd_nxt)) tp->snd_nxt = onxt; goto drop; diff --git a/freebsd/sys/netinet/tcp_output.c b/freebsd/sys/netinet/tcp_output.c index d2606fb6..30ec34de 100644 --- a/freebsd/sys/netinet/tcp_output.c +++ b/freebsd/sys/netinet/tcp_output.c @@ -132,6 +132,12 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, sendbuf_max, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(tcp_autosndbuf_max), 0, "Max size of automatic send buffer"); +VNET_DEFINE(int, tcp_sendbuf_auto_lowat) = 0; +#define V_tcp_sendbuf_auto_lowat VNET(tcp_sendbuf_auto_lowat) +SYSCTL_INT(_net_inet_tcp, OID_AUTO, sendbuf_auto_lowat, CTLFLAG_VNET | CTLFLAG_RW, + &VNET_NAME(tcp_sendbuf_auto_lowat), 0, + "Modify threshold for auto send buffer growth to account for SO_SNDLOWAT"); + /* * Make sure that either retransmit or persist timer is set for SYN, FIN and * non-ACK. @@ -382,7 +388,7 @@ after_sack_rexmit: */ if (sack_rxmit == 0) { if (sack_bytes_rxmt == 0) - len = ((int32_t)ulmin(sbavail(&so->so_snd), sendwin) - + len = ((int32_t)min(sbavail(&so->so_snd), sendwin) - off); else { int32_t cwin; @@ -523,8 +529,12 @@ after_sack_rexmit: * XXXGL: should there be used sbused() or sbavail()? */ if (V_tcp_do_autosndbuf && so->so_snd.sb_flags & SB_AUTOSIZE) { - if ((tp->snd_wnd / 4 * 5) >= so->so_snd.sb_hiwat && - sbused(&so->so_snd) >= (so->so_snd.sb_hiwat / 8 * 7) && + int autosndbuf_mod = 0; + if (V_tcp_sendbuf_auto_lowat) + autosndbuf_mod = so->so_snd.sb_lowat; + + if ((tp->snd_wnd / 4 * 5) >= so->so_snd.sb_hiwat - autosndbuf_mod && + sbused(&so->so_snd) >= (so->so_snd.sb_hiwat / 8 * 7) - autosndbuf_mod && sbused(&so->so_snd) < V_tcp_autosndbuf_max && sendwin >= (sbused(&so->so_snd) - (tp->snd_nxt - tp->snd_una))) { diff --git a/freebsd/sys/netinet/tcp_subr.c b/freebsd/sys/netinet/tcp_subr.c index 30464e1b..dff41275 100644 --- a/freebsd/sys/netinet/tcp_subr.c +++ b/freebsd/sys/netinet/tcp_subr.c @@ -281,7 +281,7 @@ find_tcp_functions_locked(struct tcp_function_set *fs) struct tcp_function_block *blk=NULL; TAILQ_FOREACH(f, &t_functions, tf_next) { - if (strcmp(f->tf_fb->tfb_tcp_block_name, fs->function_set_name) == 0) { + if (strcmp(f->tf_name, fs->function_set_name) == 0) { blk = f->tf_fb; break; } @@ -382,6 +382,7 @@ sysctl_net_inet_list_available(SYSCTL_HANDLER_ARGS) struct tcp_function *f; char *buffer, *cp; size_t bufsz, outsz; + bool alias; cnt = 0; rw_rlock(&tcp_function_lock); @@ -390,22 +391,25 @@ sysctl_net_inet_list_available(SYSCTL_HANDLER_ARGS) } rw_runlock(&tcp_function_lock); - bufsz = (cnt+2) * (TCP_FUNCTION_NAME_LEN_MAX + 12) + 1; + bufsz = (cnt+2) * ((TCP_FUNCTION_NAME_LEN_MAX * 2) + 13) + 1; buffer = malloc(bufsz, M_TEMP, M_WAITOK); error = 0; cp = buffer; - linesz = snprintf(cp, bufsz, "\n%-32s%c %s\n", "Stack", 'D', "PCB count"); + linesz = snprintf(cp, bufsz, "\n%-32s%c %-32s %s\n", "Stack", 'D', + "Alias", "PCB count"); cp += linesz; bufsz -= linesz; outsz = linesz; rw_rlock(&tcp_function_lock); TAILQ_FOREACH(f, &t_functions, tf_next) { - linesz = snprintf(cp, bufsz, "%-32s%c %u\n", + alias = (f->tf_name != f->tf_fb->tfb_tcp_block_name); + linesz = snprintf(cp, bufsz, "%-32s%c %-32s %u\n", f->tf_fb->tfb_tcp_block_name, (f->tf_fb == tcp_func_set_ptr) ? '*' : ' ', + alias ? f->tf_name : "-", f->tf_fb->tfb_refcnt); if (linesz >= bufsz) { error = EOVERFLOW; @@ -506,12 +510,31 @@ maketcp_hashsize(int size) return (hashsize); } +/* + * Register a TCP function block with the name provided in the names + * array. (Note that this function does NOT automatically register + * blk->tfb_tcp_block_name as a stack name. Therefore, you should + * explicitly include blk->tfb_tcp_block_name in the list of names if + * you wish to register the stack with that name.) + * + * Either all name registrations will succeed or all will fail. If + * a name registration fails, the function will update the num_names + * argument to point to the array index of the name that encountered + * the failure. + * + * Returns 0 on success, or an error code on failure. + */ int -register_tcp_functions(struct tcp_function_block *blk, int wait) +register_tcp_functions_as_names(struct tcp_function_block *blk, int wait, + const char *names[], int *num_names) { - struct tcp_function_block *lblk; struct tcp_function *n; struct tcp_function_set fs; + int error, i; + + KASSERT(names != NULL && *num_names > 0, + ("%s: Called with 0-length name list", __func__)); + KASSERT(names != NULL, ("%s: Called with NULL name list", __func__)); if (t_functions_inited == 0) { init_tcp_functions(); @@ -524,6 +547,7 @@ register_tcp_functions(struct tcp_function_block *blk, int wait) * These functions are required and you * need a name. */ + *num_names = 0; return (EINVAL); } if (blk->tfb_tcp_timer_stop_all || @@ -538,34 +562,99 @@ register_tcp_functions(struct tcp_function_block *blk, int wait) (blk->tfb_tcp_timer_activate == NULL) || (blk->tfb_tcp_timer_active == NULL) || (blk->tfb_tcp_timer_stop == NULL)) { - return (EINVAL); + *num_names = 0; + return (EINVAL); } - } - n = malloc(sizeof(struct tcp_function), M_TCPFUNCTIONS, wait); - if (n == NULL) { - return (ENOMEM); - } - n->tf_fb = blk; - strcpy(fs.function_set_name, blk->tfb_tcp_block_name); - rw_wlock(&tcp_function_lock); - lblk = find_tcp_functions_locked(&fs); - if (lblk) { - /* Duplicate name space not allowed */ - rw_wunlock(&tcp_function_lock); - free(n, M_TCPFUNCTIONS); - return (EALREADY); } + refcount_init(&blk->tfb_refcnt, 0); blk->tfb_flags = 0; - TAILQ_INSERT_TAIL(&t_functions, n, tf_next); - rw_wunlock(&tcp_function_lock); + for (i = 0; i < *num_names; i++) { + n = malloc(sizeof(struct tcp_function), M_TCPFUNCTIONS, wait); + if (n == NULL) { + error = ENOMEM; + goto cleanup; + } + n->tf_fb = blk; + + (void)strncpy(fs.function_set_name, names[i], + TCP_FUNCTION_NAME_LEN_MAX); + fs.function_set_name[TCP_FUNCTION_NAME_LEN_MAX - 1] = '\0'; + rw_wlock(&tcp_function_lock); + if (find_tcp_functions_locked(&fs) != NULL) { + /* Duplicate name space not allowed */ + rw_wunlock(&tcp_function_lock); + free(n, M_TCPFUNCTIONS); + error = EALREADY; + goto cleanup; + } + (void)strncpy(n->tf_name, names[i], TCP_FUNCTION_NAME_LEN_MAX); + n->tf_name[TCP_FUNCTION_NAME_LEN_MAX - 1] = '\0'; + TAILQ_INSERT_TAIL(&t_functions, n, tf_next); + rw_wunlock(&tcp_function_lock); + } return(0); -} + +cleanup: + /* + * Deregister the names we just added. Because registration failed + * for names[i], we don't need to deregister that name. + */ + *num_names = i; + rw_wlock(&tcp_function_lock); + while (--i >= 0) { + TAILQ_FOREACH(n, &t_functions, tf_next) { + if (!strncmp(n->tf_name, names[i], + TCP_FUNCTION_NAME_LEN_MAX)) { + TAILQ_REMOVE(&t_functions, n, tf_next); + n->tf_fb = NULL; + free(n, M_TCPFUNCTIONS); + break; + } + } + } + rw_wunlock(&tcp_function_lock); + return (error); +} + +/* + * Register a TCP function block using the name provided in the name + * argument. + * + * Returns 0 on success, or an error code on failure. + */ +int +register_tcp_functions_as_name(struct tcp_function_block *blk, const char *name, + int wait) +{ + const char *name_list[1]; + int num_names, rv; + + num_names = 1; + if (name != NULL) + name_list[0] = name; + else + name_list[0] = blk->tfb_tcp_block_name; + rv = register_tcp_functions_as_names(blk, wait, name_list, &num_names); + return (rv); +} + +/* + * Register a TCP function block using the name defined in + * blk->tfb_tcp_block_name. + * + * Returns 0 on success, or an error code on failure. + */ +int +register_tcp_functions(struct tcp_function_block *blk, int wait) +{ + + return (register_tcp_functions_as_name(blk, NULL, wait)); +} int deregister_tcp_functions(struct tcp_function_block *blk) { - struct tcp_function_block *lblk; struct tcp_function *f; int error=ENOENT; @@ -585,8 +674,7 @@ deregister_tcp_functions(struct tcp_function_block *blk) rw_wunlock(&tcp_function_lock); return (EBUSY); } - lblk = find_tcp_fb_locked(blk, &f); - if (lblk) { + while (find_tcp_fb_locked(blk, &f) != NULL) { /* Found */ TAILQ_REMOVE(&t_functions, f, tf_next); f->tf_fb = NULL; @@ -1582,7 +1670,6 @@ tcp_close(struct tcpcb *tp) ("tcp_close: !SS_PROTOREF")); inp->inp_flags &= ~INP_SOCKREF; INP_WUNLOCK(inp); - ACCEPT_LOCK(); SOCK_LOCK(so); so->so_state &= ~SS_PROTOREF; sofree(so); @@ -1969,16 +2056,16 @@ tcp_ctlinput(int cmd, struct sockaddr *sa, void *vip) if (inp != NULL && PRC_IS_REDIRECT(cmd)) { /* signal EHOSTDOWN, as it flushes the cached route */ inp = (*notify)(inp, EHOSTDOWN); - if (inp != NULL) - INP_WUNLOCK(inp); - } else if (inp != NULL) { + goto out; + } + icmp_tcp_seq = th->th_seq; + if (inp != NULL) { if (!(inp->inp_flags & INP_TIMEWAIT) && !(inp->inp_flags & INP_DROPPED) && !(inp->inp_socket == NULL)) { - icmp_tcp_seq = ntohl(th->th_seq); tp = intotcpcb(inp); - if (SEQ_GEQ(icmp_tcp_seq, tp->snd_una) && - SEQ_LT(icmp_tcp_seq, tp->snd_max)) { + if (SEQ_GEQ(ntohl(icmp_tcp_seq), tp->snd_una) && + SEQ_LT(ntohl(icmp_tcp_seq), tp->snd_max)) { if (cmd == PRC_MSGSIZE) { /* * MTU discovery: @@ -1986,7 +2073,7 @@ tcp_ctlinput(int cmd, struct sockaddr *sa, void *vip) * in the route to the suggested new * value (if given) and then notify. */ - mtu = ntohs(icp->icmp_nextmtu); + mtu = ntohs(icp->icmp_nextmtu); /* * If no alternative MTU was * proposed, try the next smaller @@ -2017,16 +2104,17 @@ tcp_ctlinput(int cmd, struct sockaddr *sa, void *vip) inetctlerrmap[cmd]); } } - if (inp != NULL) - INP_WUNLOCK(inp); } else { bzero(&inc, sizeof(inc)); inc.inc_fport = th->th_dport; inc.inc_lport = th->th_sport; inc.inc_faddr = faddr; inc.inc_laddr = ip->ip_src; - syncache_unreach(&inc, th); + syncache_unreach(&inc, icmp_tcp_seq); } +out: + if (inp != NULL) + INP_WUNLOCK(inp); INP_INFO_RUNLOCK(&V_tcbinfo); } #endif /* INET */ @@ -2036,7 +2124,6 @@ void tcp6_ctlinput(int cmd, struct sockaddr *sa, void *d) { struct in6_addr *dst; - struct tcphdr *th; struct inpcb *(*notify)(struct inpcb *, int) = tcp_notify; struct ip6_hdr *ip6; struct mbuf *m; @@ -2046,11 +2133,14 @@ tcp6_ctlinput(int cmd, struct sockaddr *sa, void *d) struct ip6ctlparam *ip6cp = NULL; const struct sockaddr_in6 *sa6_src = NULL; struct in_conninfo inc; + struct tcp_ports { + uint16_t th_sport; + uint16_t th_dport; + } t_ports; tcp_seq icmp_tcp_seq; unsigned int mtu; unsigned int off; - if (sa->sa_family != AF_INET6 || sa->sa_len != sizeof(struct sockaddr_in6)) return; @@ -2099,27 +2189,31 @@ tcp6_ctlinput(int cmd, struct sockaddr *sa, void *d) /* Check if we can safely get the ports from the tcp hdr */ if (m == NULL || (m->m_pkthdr.len < - (int32_t) (off + offsetof(struct tcphdr, th_seq)))) { + (int32_t) (off + sizeof(struct tcp_ports)))) { return; } - - th = (struct tcphdr *) mtodo(ip6cp->ip6c_m, ip6cp->ip6c_off); + bzero(&t_ports, sizeof(struct tcp_ports)); + m_copydata(m, off, sizeof(struct tcp_ports), (caddr_t)&t_ports); INP_INFO_RLOCK(&V_tcbinfo); - inp = in6_pcblookup(&V_tcbinfo, &ip6->ip6_dst, th->th_dport, - &ip6->ip6_src, th->th_sport, INPLOOKUP_WLOCKPCB, NULL); + inp = in6_pcblookup(&V_tcbinfo, &ip6->ip6_dst, t_ports.th_dport, + &ip6->ip6_src, t_ports.th_sport, INPLOOKUP_WLOCKPCB, NULL); if (inp != NULL && PRC_IS_REDIRECT(cmd)) { /* signal EHOSTDOWN, as it flushes the cached route */ inp = (*notify)(inp, EHOSTDOWN); - if (inp != NULL) - INP_WUNLOCK(inp); - } else if (inp != NULL) { + goto out; + } + off += sizeof(struct tcp_ports); + if (m->m_pkthdr.len < (int32_t) (off + sizeof(tcp_seq))) { + goto out; + } + m_copydata(m, off, sizeof(tcp_seq), (caddr_t)&icmp_tcp_seq); + if (inp != NULL) { if (!(inp->inp_flags & INP_TIMEWAIT) && !(inp->inp_flags & INP_DROPPED) && !(inp->inp_socket == NULL)) { - icmp_tcp_seq = ntohl(th->th_seq); tp = intotcpcb(inp); - if (SEQ_GEQ(icmp_tcp_seq, tp->snd_una) && - SEQ_LT(icmp_tcp_seq, tp->snd_max)) { + if (SEQ_GEQ(ntohl(icmp_tcp_seq), tp->snd_una) && + SEQ_LT(ntohl(icmp_tcp_seq), tp->snd_max)) { if (cmd == PRC_MSGSIZE) { /* * MTU discovery: @@ -2136,22 +2230,20 @@ tcp6_ctlinput(int cmd, struct sockaddr *sa, void *d) */ if (mtu < IPV6_MMTU) mtu = IPV6_MMTU - 8; - - bzero(&inc, sizeof(inc)); inc.inc_fibnum = M_GETFIB(m); inc.inc_flags |= INC_ISIPV6; inc.inc6_faddr = *dst; if (in6_setscope(&inc.inc6_faddr, m->m_pkthdr.rcvif, NULL)) - goto unlock_inp; - + goto out; /* * Only process the offered MTU if it * is smaller than the current one. */ if (mtu < tp->t_maxseg + - (sizeof (*th) + sizeof (*ip6))) { + sizeof (struct tcphdr) + + sizeof (struct ip6_hdr)) { tcp_hc_updatemtu(&inc, mtu); tcp_mtudisc(inp, mtu); ICMP6STAT_INC(icp6s_pmtuchg); @@ -2161,19 +2253,19 @@ tcp6_ctlinput(int cmd, struct sockaddr *sa, void *d) inet6ctlerrmap[cmd]); } } -unlock_inp: - if (inp != NULL) - INP_WUNLOCK(inp); } else { bzero(&inc, sizeof(inc)); inc.inc_fibnum = M_GETFIB(m); inc.inc_flags |= INC_ISIPV6; - inc.inc_fport = th->th_dport; - inc.inc_lport = th->th_sport; + inc.inc_fport = t_ports.th_dport; + inc.inc_lport = t_ports.th_sport; inc.inc6_faddr = *dst; inc.inc6_laddr = ip6->ip6_src; - syncache_unreach(&inc, th); + syncache_unreach(&inc, icmp_tcp_seq); } +out: + if (inp != NULL) + INP_WUNLOCK(inp); INP_INFO_RUNLOCK(&V_tcbinfo); } #endif /* INET6 */ diff --git a/freebsd/sys/netinet/tcp_syncache.c b/freebsd/sys/netinet/tcp_syncache.c index 13170ae9..cf1f0ad1 100644 --- a/freebsd/sys/netinet/tcp_syncache.c +++ b/freebsd/sys/netinet/tcp_syncache.c @@ -604,7 +604,7 @@ syncache_badack(struct in_conninfo *inc) } void -syncache_unreach(struct in_conninfo *inc, struct tcphdr *th) +syncache_unreach(struct in_conninfo *inc, tcp_seq th_seq) { struct syncache *sc; struct syncache_head *sch; @@ -615,7 +615,7 @@ syncache_unreach(struct in_conninfo *inc, struct tcphdr *th) goto done; /* If the sequence number != sc_iss, then it's a bogus ICMP msg */ - if (ntohl(th->th_seq) != sc->sc_iss) + if (ntohl(th_seq) != sc->sc_iss) goto done; /* @@ -1274,6 +1274,7 @@ syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th, * soon as possible. */ so = *lsop; + KASSERT(SOLISTENING(so), ("%s: %p not listening", __func__, so)); tp = sototcpcb(so); cred = crhold(so->so_cred); @@ -1284,7 +1285,7 @@ syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th, #endif ip_ttl = inp->inp_ip_ttl; ip_tos = inp->inp_ip_tos; - win = sbspace(&so->so_rcv); + win = so->sol_sbrcv_hiwat; ltflags = (tp->t_flags & (TF_NOOPT | TF_SIGNATURE)); #ifdef TCP_RFC7413 @@ -1297,7 +1298,7 @@ syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th, * listen queue with bogus TFO connections. */ if (atomic_fetchadd_int(tp->t_tfo_pending, 1) <= - (so->so_qlimit / 2)) { + (so->sol_qlimit / 2)) { int result; result = tcp_fastopen_check_cookie(inc, @@ -2125,7 +2126,7 @@ syncookie_lookup(struct in_conninfo *inc, struct syncache_head *sch, sc->sc_flags |= SCF_WINSCALE; } - wnd = sbspace(&lso->so_rcv); + wnd = lso->sol_sbrcv_hiwat; wnd = imax(wnd, 0); wnd = imin(wnd, TCP_MAXWIN); sc->sc_wnd = wnd; diff --git a/freebsd/sys/netinet/tcp_syncache.h b/freebsd/sys/netinet/tcp_syncache.h index ebf9fb84..3932cab7 100644 --- a/freebsd/sys/netinet/tcp_syncache.h +++ b/freebsd/sys/netinet/tcp_syncache.h @@ -38,7 +38,7 @@ void syncache_init(void); #ifdef VIMAGE void syncache_destroy(void); #endif -void syncache_unreach(struct in_conninfo *, struct tcphdr *); +void syncache_unreach(struct in_conninfo *, tcp_seq); int syncache_expand(struct in_conninfo *, struct tcpopt *, struct tcphdr *, struct socket **, struct mbuf *); int syncache_add(struct in_conninfo *, struct tcpopt *, diff --git a/freebsd/sys/netinet/tcp_timewait.c b/freebsd/sys/netinet/tcp_timewait.c index de7d08da..13105339 100644 --- a/freebsd/sys/netinet/tcp_timewait.c +++ b/freebsd/sys/netinet/tcp_timewait.c @@ -354,7 +354,6 @@ tcp_twstart(struct tcpcb *tp) ("tcp_twstart: !SS_PROTOREF")); inp->inp_flags &= ~INP_SOCKREF; INP_WUNLOCK(inp); - ACCEPT_LOCK(); SOCK_LOCK(so); so->so_state &= ~SS_PROTOREF; sofree(so); @@ -493,7 +492,6 @@ tcp_twclose(struct tcptw *tw, int reuse) if (inp->inp_flags & INP_SOCKREF) { inp->inp_flags &= ~INP_SOCKREF; INP_WUNLOCK(inp); - ACCEPT_LOCK(); SOCK_LOCK(so); KASSERT(so->so_state & SS_PROTOREF, ("tcp_twclose: INP_SOCKREF && !SS_PROTOREF")); diff --git a/freebsd/sys/netinet/tcp_var.h b/freebsd/sys/netinet/tcp_var.h index d298c9dd..2e2f88ac 100644 --- a/freebsd/sys/netinet/tcp_var.h +++ b/freebsd/sys/netinet/tcp_var.h @@ -272,8 +272,9 @@ struct tcp_function_block { }; struct tcp_function { - TAILQ_ENTRY(tcp_function) tf_next; - struct tcp_function_block *tf_fb; + TAILQ_ENTRY(tcp_function) tf_next; + char tf_name[TCP_FUNCTION_NAME_LEN_MAX]; + struct tcp_function_block *tf_fb; }; TAILQ_HEAD(tcp_funchead, tcp_function); @@ -785,6 +786,10 @@ void tcp_do_segment(struct mbuf *, struct tcphdr *, int); int register_tcp_functions(struct tcp_function_block *blk, int wait); +int register_tcp_functions_as_names(struct tcp_function_block *blk, + int wait, const char *names[], int *num_names); +int register_tcp_functions_as_name(struct tcp_function_block *blk, + const char *name, int wait); int deregister_tcp_functions(struct tcp_function_block *blk); struct tcp_function_block *find_and_ref_tcp_functions(struct tcp_function_set *fs); struct tcp_function_block *find_and_ref_tcp_fb(struct tcp_function_block *blk); diff --git a/freebsd/sys/netinet6/ip6_output.c b/freebsd/sys/netinet6/ip6_output.c index 48a8c454..d8ed24a9 100644 --- a/freebsd/sys/netinet6/ip6_output.c +++ b/freebsd/sys/netinet6/ip6_output.c @@ -119,10 +119,6 @@ __FBSDID("$FreeBSD$"); #include <netinet6/ip6protosw.h> #include <netinet6/scope6_var.h> -#ifdef FLOWTABLE -#include <net/flowtable.h> -#endif - extern int in6_mcast_loop; struct ip6_exthdrs { @@ -504,10 +500,6 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, if (opt && opt->ip6po_rthdr) ro = &opt->ip6po_route; dst = (struct sockaddr_in6 *)&ro->ro_dst; -#ifdef FLOWTABLE - if (ro->ro_rt == NULL) - (void )flowtable_lookup(AF_INET6, m, (struct route *)ro); -#endif fibnum = (inp != NULL) ? inp->inp_inc.inc_fibnum : M_GETFIB(m); again: /* diff --git a/freebsd/sys/netinet6/sctp6_usrreq.c b/freebsd/sys/netinet6/sctp6_usrreq.c index f94068f9..cca2ebcf 100644 --- a/freebsd/sys/netinet6/sctp6_usrreq.c +++ b/freebsd/sys/netinet6/sctp6_usrreq.c @@ -307,7 +307,7 @@ sctp6_ctlinput(int cmd, struct sockaddr *pktdst, void *d) return; } /* Copy out the port numbers and the verification tag. */ - bzero(&sh, sizeof(sh)); + memset(&sh, 0, sizeof(sh)); m_copydata(ip6cp->ip6c_m, ip6cp->ip6c_off, sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t), diff --git a/freebsd/sys/netipsec/ipsec.h b/freebsd/sys/netipsec/ipsec.h index 0522b7e7..a4c3f3d2 100644 --- a/freebsd/sys/netipsec/ipsec.h +++ b/freebsd/sys/netipsec/ipsec.h @@ -253,8 +253,9 @@ struct ipsecstat { #include <sys/counter.h> struct ipsec_ctx_data; -#define IPSEC_INIT_CTX(_ctx, _mp, _sav, _af, _enc) do { \ +#define IPSEC_INIT_CTX(_ctx, _mp, _inp, _sav, _af, _enc) do { \ (_ctx)->mp = (_mp); \ + (_ctx)->inp = (_inp); \ (_ctx)->sav = (_sav); \ (_ctx)->af = (_af); \ (_ctx)->enc = (_enc); \ diff --git a/freebsd/sys/netipsec/ipsec_input.c b/freebsd/sys/netipsec/ipsec_input.c index d9dfd254..38341346 100644 --- a/freebsd/sys/netipsec/ipsec_input.c +++ b/freebsd/sys/netipsec/ipsec_input.c @@ -327,7 +327,7 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, (prot == IPPROTO_UDP || prot == IPPROTO_TCP)) udp_ipsec_adjust_cksum(m, sav, prot, skip); - IPSEC_INIT_CTX(&ctx, &m, sav, AF_INET, IPSEC_ENC_BEFORE); + IPSEC_INIT_CTX(&ctx, &m, NULL, sav, AF_INET, IPSEC_ENC_BEFORE); if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0) goto bad; ip = mtod(m, struct ip *); /* update pointer */ @@ -418,7 +418,7 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, goto bad; } - IPSEC_INIT_CTX(&ctx, &m, sav, af, IPSEC_ENC_AFTER); + IPSEC_INIT_CTX(&ctx, &m, NULL, sav, af, IPSEC_ENC_AFTER); if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0) goto bad; @@ -524,7 +524,7 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, goto bad; } - IPSEC_INIT_CTX(&ctx, &m, sav, af, IPSEC_ENC_BEFORE); + IPSEC_INIT_CTX(&ctx, &m, NULL, sav, af, IPSEC_ENC_BEFORE); if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0) goto bad; @@ -595,7 +595,7 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, else #endif af = AF_INET6; - IPSEC_INIT_CTX(&ctx, &m, sav, af, IPSEC_ENC_AFTER); + IPSEC_INIT_CTX(&ctx, &m, NULL, sav, af, IPSEC_ENC_AFTER); if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0) goto bad; if (skip == 0) { diff --git a/freebsd/sys/netipsec/ipsec_output.c b/freebsd/sys/netipsec/ipsec_output.c index b7dd8f30..07e39a8a 100644 --- a/freebsd/sys/netipsec/ipsec_output.c +++ b/freebsd/sys/netipsec/ipsec_output.c @@ -183,7 +183,8 @@ next: * IPsec output logic for IPv4. */ static int -ipsec4_perform_request(struct mbuf *m, struct secpolicy *sp, u_int idx) +ipsec4_perform_request(struct mbuf *m, struct secpolicy *sp, + struct inpcb *inp, u_int idx) { struct ipsec_ctx_data ctx; union sockaddr_union *dst; @@ -213,7 +214,7 @@ ipsec4_perform_request(struct mbuf *m, struct secpolicy *sp, u_int idx) /* * XXXAE: most likely ip_sum at this point is wrong. */ - IPSEC_INIT_CTX(&ctx, &m, sav, AF_INET, IPSEC_ENC_BEFORE); + IPSEC_INIT_CTX(&ctx, &m, inp, sav, AF_INET, IPSEC_ENC_BEFORE); if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0) goto bad; @@ -237,9 +238,10 @@ ipsec4_perform_request(struct mbuf *m, struct secpolicy *sp, u_int idx) /* XXXAE: IPSEC_OSTAT_INC(tunnel); */ goto bad; } + inp = NULL; } - IPSEC_INIT_CTX(&ctx, &m, sav, dst->sa.sa_family, IPSEC_ENC_AFTER); + IPSEC_INIT_CTX(&ctx, &m, inp, sav, dst->sa.sa_family, IPSEC_ENC_AFTER); if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0) goto bad; @@ -287,7 +289,7 @@ ipsec4_process_packet(struct mbuf *m, struct secpolicy *sp, struct inpcb *inp) { - return (ipsec4_perform_request(m, sp, 0)); + return (ipsec4_perform_request(m, sp, inp, 0)); } static int @@ -493,7 +495,8 @@ next: * IPsec output logic for IPv6. */ static int -ipsec6_perform_request(struct mbuf *m, struct secpolicy *sp, u_int idx) +ipsec6_perform_request(struct mbuf *m, struct secpolicy *sp, + struct inpcb *inp, u_int idx) { struct ipsec_ctx_data ctx; union sockaddr_union *dst; @@ -516,7 +519,7 @@ ipsec6_perform_request(struct mbuf *m, struct secpolicy *sp, u_int idx) ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6)); - IPSEC_INIT_CTX(&ctx, &m, sav, AF_INET6, IPSEC_ENC_BEFORE); + IPSEC_INIT_CTX(&ctx, &m, inp, sav, AF_INET6, IPSEC_ENC_BEFORE); if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0) goto bad; @@ -542,9 +545,10 @@ ipsec6_perform_request(struct mbuf *m, struct secpolicy *sp, u_int idx) /* XXXAE: IPSEC_OSTAT_INC(tunnel); */ goto bad; } + inp = NULL; } - IPSEC_INIT_CTX(&ctx, &m, sav, dst->sa.sa_family, IPSEC_ENC_AFTER); + IPSEC_INIT_CTX(&ctx, &m, inp, sav, dst->sa.sa_family, IPSEC_ENC_AFTER); if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0) goto bad; @@ -587,7 +591,7 @@ ipsec6_process_packet(struct mbuf *m, struct secpolicy *sp, struct inpcb *inp) { - return (ipsec6_perform_request(m, sp, 0)); + return (ipsec6_perform_request(m, sp, inp, 0)); } static int @@ -752,14 +756,14 @@ ipsec_process_done(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav, case AF_INET: key_freesav(&sav); IPSECSTAT_INC(ips_out_bundlesa); - return (ipsec4_perform_request(m, sp, idx)); + return (ipsec4_perform_request(m, sp, NULL, idx)); /* NOTREACHED */ #endif #ifdef INET6 case AF_INET6: key_freesav(&sav); IPSEC6STAT_INC(ips_out_bundlesa); - return (ipsec6_perform_request(m, sp, idx)); + return (ipsec6_perform_request(m, sp, NULL, idx)); /* NOTREACHED */ #endif /* INET6 */ default: diff --git a/freebsd/sys/netipsec/key_debug.c b/freebsd/sys/netipsec/key_debug.c index 1911af01..f71212b7 100644 --- a/freebsd/sys/netipsec/key_debug.c +++ b/freebsd/sys/netipsec/key_debug.c @@ -79,10 +79,6 @@ static void kdebug_sadb_x_sa2(struct sadb_ext *); static void kdebug_sadb_x_sa_replay(struct sadb_ext *); static void kdebug_sadb_x_natt(struct sadb_ext *); -#ifdef _KERNEL -static void kdebug_secreplay(struct secreplay *); -#endif - #ifndef _KERNEL #define panic(fmt, ...) { printf(fmt, ## __VA_ARGS__); exit(-1); } #endif @@ -726,6 +722,7 @@ kdebug_secash(struct secashead *sah, const char *indent) printf("}\n"); } +#ifdef IPSEC_DEBUG static void kdebug_secreplay(struct secreplay *rpl) { @@ -747,6 +744,7 @@ kdebug_secreplay(struct secreplay *rpl) } printf(" }\n"); } +#endif /* IPSEC_DEBUG */ static void kdebug_secnatt(struct secnatt *natt) diff --git a/freebsd/sys/netpfil/pf/pf.c b/freebsd/sys/netpfil/pf/pf.c index 42f9132d..ca46d16e 100644 --- a/freebsd/sys/netpfil/pf/pf.c +++ b/freebsd/sys/netpfil/pf/pf.c @@ -134,6 +134,9 @@ VNET_DEFINE(int, pf_tcp_iss_off); VNET_DECLARE(int, pf_vnet_active); #define V_pf_vnet_active VNET(pf_vnet_active) +static VNET_DEFINE(uint32_t, pf_purge_idx); +#define V_pf_purge_idx VNET(pf_purge_idx) + /* * Queue for pf_intr() sends. */ @@ -1429,7 +1432,6 @@ void pf_purge_thread(void *unused __unused) { VNET_ITERATOR_DECL(vnet_iter); - u_int idx = 0; sx_xlock(&pf_end_lock); while (pf_end_threads == 0) { @@ -1450,14 +1452,15 @@ pf_purge_thread(void *unused __unused) * Process 1/interval fraction of the state * table every run. */ - idx = pf_purge_expired_states(idx, pf_hashmask / + V_pf_purge_idx = + pf_purge_expired_states(V_pf_purge_idx, pf_hashmask / (V_pf_default_rule.timeout[PFTM_INTERVAL] * 10)); /* * Purge other expired types every * PFTM_INTERVAL seconds. */ - if (idx == 0) { + if (V_pf_purge_idx == 0) { /* * Order is important: * - states and src nodes reference rules diff --git a/freebsd/sys/sys/_pctrie.h b/freebsd/sys/sys/_pctrie.h index 45f69b20..c6d13baa 100644 --- a/freebsd/sys/sys/_pctrie.h +++ b/freebsd/sys/sys/_pctrie.h @@ -38,14 +38,4 @@ struct pctrie { uintptr_t pt_root; }; -#ifdef _KERNEL - -static __inline boolean_t -pctrie_is_empty(struct pctrie *ptree) -{ - - return (ptree->pt_root == 0); -} - -#endif /* _KERNEL */ #endif /* !__SYS_PCTRIE_H_ */ diff --git a/freebsd/sys/sys/ata.h b/freebsd/sys/sys/ata.h index 0ed78ec8..4d3b3e7f 100644 --- a/freebsd/sys/sys/ata.h +++ b/freebsd/sys/sys/ata.h @@ -68,8 +68,8 @@ struct ata_params { /*049*/ u_int16_t capabilities1; #define ATA_SUPPORT_DMA 0x0100 #define ATA_SUPPORT_LBA 0x0200 -#define ATA_SUPPORT_IORDY 0x0400 -#define ATA_SUPPORT_IORDYDIS 0x0800 +#define ATA_SUPPORT_IORDYDIS 0x0400 +#define ATA_SUPPORT_IORDY 0x0800 #define ATA_SUPPORT_OVERLAP 0x4000 /*050*/ u_int16_t capabilities2; diff --git a/freebsd/sys/sys/blist.h b/freebsd/sys/sys/blist.h index f2e81ec8..292f9b97 100644 --- a/freebsd/sys/sys/blist.h +++ b/freebsd/sys/sys/blist.h @@ -44,7 +44,7 @@ * ops. * * SWAPBLK_NONE is returned on failure. This module is typically - * capable of managing up to (2^31) blocks per blist, though + * capable of managing up to (2^63) blocks per blist, though * the memory utilization would be insane if you actually did * that. Managing something like 512MB worth of 4K blocks * eats around 32 KBytes of memory. @@ -56,7 +56,7 @@ #ifndef _SYS_BLIST_H_ #define _SYS_BLIST_H_ -typedef u_int32_t u_daddr_t; /* unsigned disk address */ +typedef uint64_t u_daddr_t; /* unsigned disk address */ /* * note: currently use SWAPBLK_NONE as an absolute value rather then @@ -67,7 +67,7 @@ typedef u_int32_t u_daddr_t; /* unsigned disk address */ #define SWAPBLK_NONE ((daddr_t)((u_daddr_t)SWAPBLK_MASK + 1))/* flag */ /* - * blmeta and bl_bitmap_t MUST be a power of 2 in size. + * Both blmeta and bmu_bitmap MUST be a power of 2 in size. */ typedef struct blmeta { @@ -82,9 +82,8 @@ typedef struct blist { daddr_t bl_blocks; /* area of coverage */ daddr_t bl_radix; /* coverage radix */ daddr_t bl_skip; /* starting skip */ - daddr_t bl_free; /* number of free blocks */ + daddr_t bl_cursor; /* next-fit search starts at */ blmeta_t *bl_root; /* root of radix tree */ - daddr_t bl_rootblks; /* daddr_t blks allocated for tree */ } *blist_t; #define BLIST_META_RADIX 16 @@ -92,13 +91,14 @@ typedef struct blist { #define BLIST_MAX_ALLOC BLIST_BMAP_RADIX -extern blist_t blist_create(daddr_t blocks, int flags); -extern void blist_destroy(blist_t blist); -extern daddr_t blist_alloc(blist_t blist, daddr_t count); -extern void blist_free(blist_t blist, daddr_t blkno, daddr_t count); -extern int blist_fill(blist_t bl, daddr_t blkno, daddr_t count); -extern void blist_print(blist_t blist); -extern void blist_resize(blist_t *pblist, daddr_t count, int freenew, int flags); +daddr_t blist_alloc(blist_t blist, daddr_t count); +daddr_t blist_avail(blist_t blist); +blist_t blist_create(daddr_t blocks, int flags); +void blist_destroy(blist_t blist); +daddr_t blist_fill(blist_t bl, daddr_t blkno, daddr_t count); +void blist_free(blist_t blist, daddr_t blkno, daddr_t count); +void blist_print(blist_t blist); +void blist_resize(blist_t *pblist, daddr_t count, int freenew, int flags); #endif /* _SYS_BLIST_H_ */ diff --git a/freebsd/sys/sys/buf.h b/freebsd/sys/sys/buf.h index 830fb054..58bd91e3 100644 --- a/freebsd/sys/sys/buf.h +++ b/freebsd/sys/sys/buf.h @@ -472,6 +472,7 @@ buf_track(struct buf *bp, const char *location) extern int nbuf; /* The number of buffer headers */ extern long maxswzone; /* Max KVA for swap structures */ extern long maxbcache; /* Max KVA for buffer cache */ +extern int maxbcachebuf; /* Max buffer cache block size */ extern long runningbufspace; extern long hibufspace; extern int dirtybufthresh; diff --git a/freebsd/sys/sys/bus_dma.h b/freebsd/sys/sys/bus_dma.h index 1a2ecd6b..04dbe0cb 100644 --- a/freebsd/sys/sys/bus_dma.h +++ b/freebsd/sys/sys/bus_dma.h @@ -248,105 +248,49 @@ int bus_dmamap_load_ma_triv(bus_dma_tag_t dmat, bus_dmamap_t map, struct vm_page **ma, bus_size_t tlen, int ma_offs, int flags, bus_dma_segment_t *segs, int *segp); -/* - * XXX sparc64 uses the same interface, but a much different implementation. - * <machine/bus_dma.h> for the sparc64 arch contains the equivalent - * declarations. - */ -#if !defined(__sparc64__) +#ifdef WANT_INLINE_DMAMAP +#define BUS_DMAMAP_OP static inline +#else +#define BUS_DMAMAP_OP +#endif /* * Allocate a handle for mapping from kva/uva/physical * address space into bus device space. */ -int bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp); +BUS_DMAMAP_OP int bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp); /* * Destroy a handle for mapping from kva/uva/physical * address space into bus device space. */ -int bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map); +BUS_DMAMAP_OP int bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map); /* * Allocate a piece of memory that can be efficiently mapped into * bus device space based on the constraints listed in the dma tag. * A dmamap to for use with dmamap_load is also allocated. */ -int bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags, +BUS_DMAMAP_OP int bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags, bus_dmamap_t *mapp); /* * Free a piece of memory and its allocated dmamap, that was allocated * via bus_dmamem_alloc. */ -void bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map); +BUS_DMAMAP_OP void bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map); /* * Perform a synchronization operation on the given map. If the map - * is NULL we have a fully IO-coherent system. On every ARM architecture - * there must be a memory barrier placed to ensure that all data - * accesses are visible before going any further. + * is NULL we have a fully IO-coherent system. */ -void _bus_dmamap_sync(bus_dma_tag_t, bus_dmamap_t, bus_dmasync_op_t); -#if defined(__arm__) - #define __BUS_DMAMAP_SYNC_DEFAULT mb() -#elif defined(__aarch64__) - #define __BUS_DMAMAP_SYNC_DEFAULT dmb(sy) -#else - #define __BUS_DMAMAP_SYNC_DEFAULT do {} while (0) -#endif -#define bus_dmamap_sync(dmat, dmamap, op) \ - do { \ - if ((dmamap) != NULL) \ - _bus_dmamap_sync(dmat, dmamap, op); \ - else \ - __BUS_DMAMAP_SYNC_DEFAULT; \ - } while (0) +BUS_DMAMAP_OP void bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t dmamap, bus_dmasync_op_t op); /* * Release the mapping held by map. */ -void _bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map); -#define bus_dmamap_unload(dmat, dmamap) \ - do { \ - if ((dmamap) != NULL) \ - _bus_dmamap_unload(dmat, dmamap); \ - } while (0) - -/* - * The following functions define the interface between the MD and MI - * busdma layers. These are not intended for consumption by driver - * software. - */ -void __bus_dmamap_waitok(bus_dma_tag_t dmat, bus_dmamap_t map, - struct memdesc *mem, - bus_dmamap_callback_t *callback, - void *callback_arg); - -#define _bus_dmamap_waitok(dmat, map, mem, callback, callback_arg) \ - do { \ - if ((map) != NULL) \ - __bus_dmamap_waitok(dmat, map, mem, callback, \ - callback_arg); \ - } while (0); - -int _bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dmamap_t map, - void *buf, bus_size_t buflen, struct pmap *pmap, - int flags, bus_dma_segment_t *segs, int *segp); - -int _bus_dmamap_load_phys(bus_dma_tag_t dmat, bus_dmamap_t map, - vm_paddr_t paddr, bus_size_t buflen, - int flags, bus_dma_segment_t *segs, int *segp); - -int _bus_dmamap_load_ma(bus_dma_tag_t dmat, bus_dmamap_t map, - struct vm_page **ma, bus_size_t tlen, int ma_offs, int flags, - bus_dma_segment_t *segs, int *segp); - -bus_dma_segment_t *_bus_dmamap_complete(bus_dma_tag_t dmat, - bus_dmamap_t map, - bus_dma_segment_t *segs, - int nsegs, int error); +BUS_DMAMAP_OP void bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t dmamap); -#endif /* __sparc64__ */ +#undef BUS_DMAMAP_OP #endif /* _BUS_DMA_H_ */ diff --git a/freebsd/sys/sys/jail.h b/freebsd/sys/sys/jail.h index c67c8f55..bc9025cf 100644 --- a/freebsd/sys/sys/jail.h +++ b/freebsd/sys/sys/jail.h @@ -215,22 +215,24 @@ struct prison_racct { /* by this jail or an ancestor */ /* Flags for pr_allow */ -#define PR_ALLOW_SET_HOSTNAME 0x0001 -#define PR_ALLOW_SYSVIPC 0x0002 -#define PR_ALLOW_RAW_SOCKETS 0x0004 -#define PR_ALLOW_CHFLAGS 0x0008 -#define PR_ALLOW_MOUNT 0x0010 -#define PR_ALLOW_QUOTAS 0x0020 -#define PR_ALLOW_SOCKET_AF 0x0040 -#define PR_ALLOW_MOUNT_DEVFS 0x0080 -#define PR_ALLOW_MOUNT_NULLFS 0x0100 -#define PR_ALLOW_MOUNT_ZFS 0x0200 -#define PR_ALLOW_MOUNT_PROCFS 0x0400 -#define PR_ALLOW_MOUNT_TMPFS 0x0800 -#define PR_ALLOW_MOUNT_FDESCFS 0x1000 -#define PR_ALLOW_MOUNT_LINPROCFS 0x2000 -#define PR_ALLOW_MOUNT_LINSYSFS 0x4000 -#define PR_ALLOW_ALL 0x7fff +#define PR_ALLOW_SET_HOSTNAME 0x00000001 +#define PR_ALLOW_SYSVIPC 0x00000002 +#define PR_ALLOW_RAW_SOCKETS 0x00000004 +#define PR_ALLOW_CHFLAGS 0x00000008 +#define PR_ALLOW_MOUNT 0x00000010 +#define PR_ALLOW_QUOTAS 0x00000020 +#define PR_ALLOW_SOCKET_AF 0x00000040 +#define PR_ALLOW_MOUNT_DEVFS 0x00000080 +#define PR_ALLOW_MOUNT_NULLFS 0x00000100 +#define PR_ALLOW_MOUNT_ZFS 0x00000200 +#define PR_ALLOW_MOUNT_PROCFS 0x00000400 +#define PR_ALLOW_MOUNT_TMPFS 0x00000800 +#define PR_ALLOW_MOUNT_FDESCFS 0x00001000 +#define PR_ALLOW_MOUNT_LINPROCFS 0x00002000 +#define PR_ALLOW_MOUNT_LINSYSFS 0x00004000 +#define PR_ALLOW_RESERVED_PORTS 0x00008000 +#define PR_ALLOW_KMEM_ACCESS 0x00010000 /* reserved, not used yet */ +#define PR_ALLOW_ALL 0x0001ffff /* * OSD methods diff --git a/freebsd/sys/sys/module.h b/freebsd/sys/sys/module.h index 71aa9954..5a268fc1 100644 --- a/freebsd/sys/sys/module.h +++ b/freebsd/sys/sys/module.h @@ -106,14 +106,15 @@ struct mod_pnp_match_info #include <sys/linker_set.h> +#define MODULE_METADATA_CONCAT(uniquifier) _mod_metadata##uniquifier #define MODULE_METADATA(uniquifier, type, data, cval) \ - static struct mod_metadata _mod_metadata##uniquifier = { \ + static struct mod_metadata MODULE_METADATA_CONCAT(uniquifier) = { \ MDT_STRUCT_VERSION, \ type, \ data, \ cval \ }; \ - DATA_SET(modmetadata_set, _mod_metadata##uniquifier) + DATA_SET(modmetadata_set, MODULE_METADATA_CONCAT(uniquifier)) #define MODULE_DEPEND(module, mdepend, vmin, vpref, vmax) \ static struct mod_depend _##module##_depend_on_##mdepend \ @@ -139,7 +140,7 @@ struct mod_pnp_match_info #define DECLARE_MODULE_WITH_MAXVER(name, data, sub, order, maxver) \ MODULE_DEPEND(name, kernel, __FreeBSD_version, \ __FreeBSD_version, maxver); \ - MODULE_METADATA(_md_##name, MDT_MODULE, &data, #name); \ + MODULE_METADATA(_md_##name, MDT_MODULE, &data, __XSTRING(name));\ SYSINIT(name##module, sub, order, module_register_init, &data); \ struct __hack @@ -156,13 +157,14 @@ struct mod_pnp_match_info #define DECLARE_MODULE_TIED(name, data, sub, order) \ DECLARE_MODULE_WITH_MAXVER(name, data, sub, order, __FreeBSD_version) +#define MODULE_VERSION_CONCAT(module, version) _##module##_version #define MODULE_VERSION(module, version) \ - static struct mod_version _##module##_version \ + static struct mod_version MODULE_VERSION_CONCAT(module, version)\ __section(".data") = { \ version \ }; \ - MODULE_METADATA(_##module##_version, MDT_VERSION, \ - &_##module##_version, #module) + MODULE_METADATA(MODULE_VERSION_CONCAT(module, version), MDT_VERSION,\ + &MODULE_VERSION_CONCAT(module, version), __XSTRING(module)) /** * Generic macros to create pnp info hints that modules may export diff --git a/freebsd/sys/sys/proc.h b/freebsd/sys/sys/proc.h index 0644d68b..b28c19e6 100644 --- a/freebsd/sys/sys/proc.h +++ b/freebsd/sys/sys/proc.h @@ -142,6 +142,7 @@ struct pargs { * j - locked by proc slock * k - only accessed by curthread * k*- only accessed by curthread and from an interrupt + * kx- only accessed by curthread and by debugger * l - the attaching proc or attaching proc parent * m - Giant * n - not locked, lazy @@ -327,11 +328,11 @@ struct thread { u_char td_pri_class; /* (t) Scheduling class. */ u_char td_user_pri; /* (t) User pri from estcpu and nice. */ u_char td_base_user_pri; /* (t) Base user pri */ - u_int td_dbg_sc_code; /* (c) Syscall code to debugger. */ - u_int td_dbg_sc_narg; /* (c) Syscall arg count to debugger.*/ uintptr_t td_rb_list; /* (k) Robust list head. */ uintptr_t td_rbp_list; /* (k) Robust priv list head. */ uintptr_t td_rb_inact; /* (k) Current in-action mutex loc. */ + struct syscall_args td_sa; /* (kx) Syscall parameters. Copied on + fork for child tracing. */ #define td_endcopy td_pcb /* @@ -1119,7 +1120,7 @@ void userret(struct thread *, struct trapframe *); void cpu_exit(struct thread *); void exit1(struct thread *, int, int) __dead2; void cpu_copy_thread(struct thread *td, struct thread *td0); -int cpu_fetch_syscall_args(struct thread *td, struct syscall_args *sa); +int cpu_fetch_syscall_args(struct thread *td); void cpu_fork(struct thread *, struct proc *, struct thread *, int); void cpu_fork_kthread_handler(struct thread *, void (*)(void *), void *); void cpu_set_syscall_retval(struct thread *, int); diff --git a/freebsd/sys/sys/refcount.h b/freebsd/sys/sys/refcount.h index 32f7f6e4..ea846f8c 100644 --- a/freebsd/sys/sys/refcount.h +++ b/freebsd/sys/sys/refcount.h @@ -50,7 +50,7 @@ refcount_acquire(volatile u_int *count) { KASSERT(*count < UINT_MAX, ("refcount %p overflowed", count)); - atomic_add_acq_int((volatile int *)count, 1); + atomic_add_int((volatile int *)count, 1); } static __inline int @@ -58,10 +58,20 @@ refcount_release(volatile u_int *count) { u_int old; - /* XXX: Should this have a rel membar? */ + atomic_thread_fence_rel(); old = atomic_fetchadd_int((volatile int *)count, -1); KASSERT(old > 0, ("negative refcount %p", count)); - return (old == 1); + if (old > 1) + return (0); + + /* + * Last reference. Signal the user to call the destructor. + * + * Ensure that the destructor sees all updates. The fence_rel + * at the start of the function synchronized with this fence. + */ + atomic_thread_fence_acq(); + return (1); } #endif /* ! __SYS_REFCOUNT_H__ */ diff --git a/freebsd/sys/sys/sockbuf.h b/freebsd/sys/sys/sockbuf.h index b1ebe62f..c9cb4dd7 100644 --- a/freebsd/sys/sys/sockbuf.h +++ b/freebsd/sys/sys/sockbuf.h @@ -32,7 +32,6 @@ */ #ifndef _SYS_SOCKBUF_H_ #define _SYS_SOCKBUF_H_ -#include <sys/selinfo.h> /* for struct selinfo */ #include <sys/_lock.h> #include <sys/_mutex.h> #include <sys/_sx.h> @@ -64,6 +63,7 @@ struct mbuf; struct sockaddr; struct socket; struct thread; +struct selinfo; struct xsockbuf { u_int sb_cc; @@ -84,9 +84,9 @@ struct xsockbuf { * (a) locked by SOCKBUF_LOCK(). */ struct sockbuf { - struct selinfo sb_sel; /* process selecting read/write */ - struct mtx sb_mtx; /* sockbuf lock */ - struct sx sb_sx; /* prevent I/O interlacing */ + struct mtx sb_mtx; /* sockbuf lock */ + struct sx sb_sx; /* prevent I/O interlacing */ + struct selinfo *sb_sel; /* process selecting read/write */ short sb_state; /* (a) socket state on sockbuf */ #define sb_startzero sb_mb struct mbuf *sb_mb; /* (a) the mbuf chain */ @@ -169,8 +169,7 @@ void sbflush_locked(struct sockbuf *sb); void sbrelease(struct sockbuf *sb, struct socket *so); void sbrelease_internal(struct sockbuf *sb, struct socket *so); void sbrelease_locked(struct sockbuf *sb, struct socket *so); -int sbreserve(struct sockbuf *sb, u_long cc, struct socket *so, - struct thread *td); +int sbsetopt(struct socket *so, int cmd, u_long cc); int sbreserve_locked(struct sockbuf *sb, u_long cc, struct socket *so, struct thread *td); struct mbuf * diff --git a/freebsd/sys/sys/socketvar.h b/freebsd/sys/sys/socketvar.h index 245a687b..0c5b95ec 100644 --- a/freebsd/sys/sys/socketvar.h +++ b/freebsd/sys/sys/socketvar.h @@ -55,7 +55,8 @@ struct vnet; * handle on protocol and pointer to protocol * private data and error information. */ -typedef u_quad_t so_gen_t; +typedef uint64_t so_gen_t; +typedef int so_upcall_t(struct socket *, void *, int); struct socket; @@ -63,60 +64,35 @@ struct socket; * Locking key to struct socket: * (a) constant after allocation, no locking required. * (b) locked by SOCK_LOCK(so). - * (c) locked by SOCKBUF_LOCK(&so->so_rcv). - * (e) locked by ACCEPT_LOCK(). + * (cr) locked by SOCKBUF_LOCK(&so->so_rcv). + * (cs) locked by SOCKBUF_LOCK(&so->so_rcv). + * (e) locked by SOLISTEN_LOCK() of corresponding listening socket. * (f) not locked since integer reads/writes are atomic. * (g) used only as a sleep/wakeup address, no value. * (h) locked by global mutex so_global_mtx. */ +TAILQ_HEAD(accept_queue, socket); struct socket { - int so_count; /* (b) reference count */ + struct mtx so_lock; + volatile u_int so_count; /* (b / refcount) */ + struct selinfo so_rdsel; /* (b/cr) for so_rcv/so_comp */ + struct selinfo so_wrsel; /* (b/cs) for so_snd */ short so_type; /* (a) generic type, see socket.h */ - short so_options; /* from socket call, see socket.h */ - short so_linger; /* time to linger while closing */ + short so_options; /* (b) from socket call, see socket.h */ + short so_linger; /* time to linger close(2) */ short so_state; /* (b) internal state flags SS_* */ - int so_qstate; /* (e) internal state flags SQ_* */ void *so_pcb; /* protocol control block */ struct vnet *so_vnet; /* (a) network stack instance */ struct protosw *so_proto; /* (a) protocol handle */ -/* - * Variables for connection queuing. - * Socket where accepts occur is so_head in all subsidiary sockets. - * If so_head is 0, socket is not related to an accept. - * For head socket so_incomp queues partially completed connections, - * while so_comp is a queue of connections ready to be accepted. - * If a connection is aborted and it has so_head set, then - * it has to be pulled out of either so_incomp or so_comp. - * We allow connections to queue up based on current queue lengths - * and limit on number of queued connections for this socket. - */ - struct socket *so_head; /* (e) back pointer to listen socket */ - TAILQ_HEAD(, socket) so_incomp; /* (e) queue of partial unaccepted connections */ - TAILQ_HEAD(, socket) so_comp; /* (e) queue of complete unaccepted connections */ - TAILQ_ENTRY(socket) so_list; /* (e) list of unaccepted connections */ - u_int so_qlen; /* (e) number of unaccepted connections */ - u_int so_incqlen; /* (e) number of unaccepted incomplete - connections */ - u_int so_qlimit; /* (e) max number queued connections */ short so_timeo; /* (g) connection timeout */ u_short so_error; /* (f) error affecting connection */ struct sigio *so_sigio; /* [sg] information for async I/O or out of band data (SIGURG) */ - u_long so_oobmark; /* (c) chars to oob mark */ - - struct sockbuf so_rcv, so_snd; - struct ucred *so_cred; /* (a) user credentials */ struct label *so_label; /* (b) MAC label for socket */ - struct label *so_peerlabel; /* (b) cached MAC label for peer */ /* NB: generation count must not be first. */ so_gen_t so_gencnt; /* (h) generation count */ void *so_emuldata; /* (b) private data for emulators */ - struct so_accf { - struct accept_filter *so_accept_filter; - void *so_accept_filter_arg; /* saved filter args */ - char *so_accept_filter_str; /* saved user args */ - } *so_accf; struct osd osd; /* Object Specific extensions */ /* * so_fibnum, so_user_cookie and friends can be used to attach @@ -129,39 +105,93 @@ struct socket { int so_ts_clock; /* type of the clock used for timestamps */ uint32_t so_max_pacing_rate; /* (f) TX rate limit in bytes/s */ - - void *so_pspare[2]; /* general use */ - int so_ispare[2]; /* general use */ + union { + /* Regular (data flow) socket. */ + struct { + /* (cr, cs) Receive and send buffers. */ + struct sockbuf so_rcv, so_snd; + + /* (e) Our place on accept queue. */ + TAILQ_ENTRY(socket) so_list; + struct socket *so_listen; /* (b) */ + enum { + SQ_NONE = 0, + SQ_INCOMP = 0x0800, /* on sol_incomp */ + SQ_COMP = 0x1000, /* on sol_comp */ + } so_qstate; /* (b) */ + + /* (b) cached MAC label for peer */ + struct label *so_peerlabel; + u_long so_oobmark; /* chars to oob mark */ + }; + /* + * Listening socket, where accepts occur, is so_listen in all + * subsidiary sockets. If so_listen is NULL, socket is not + * related to an accept. For a listening socket itself + * sol_incomp queues partially completed connections, while + * sol_comp is a queue of connections ready to be accepted. + * If a connection is aborted and it has so_listen set, then + * it has to be pulled out of either sol_incomp or sol_comp. + * We allow connections to queue up based on current queue + * lengths and limit on number of queued connections for this + * socket. + */ + struct { + /* (e) queue of partial unaccepted connections */ + struct accept_queue sol_incomp; + /* (e) queue of complete unaccepted connections */ + struct accept_queue sol_comp; + u_int sol_qlen; /* (e) sol_comp length */ + u_int sol_incqlen; /* (e) sol_incomp length */ + u_int sol_qlimit; /* (e) queue limit */ + + /* accept_filter(9) optional data */ + struct accept_filter *sol_accept_filter; + void *sol_accept_filter_arg; /* saved filter args */ + char *sol_accept_filter_str; /* saved user args */ + + /* Optional upcall, for kernel socket. */ + so_upcall_t *sol_upcall; /* (e) */ + void *sol_upcallarg; /* (e) */ + + /* Socket buffer parameters, to be copied to + * dataflow sockets, accepted from this one. */ + int sol_sbrcv_lowat; + int sol_sbsnd_lowat; + u_int sol_sbrcv_hiwat; + u_int sol_sbsnd_hiwat; + short sol_sbrcv_flags; + short sol_sbsnd_flags; + sbintime_t sol_sbrcv_timeo; + sbintime_t sol_sbsnd_timeo; + }; + }; }; -/* - * Global accept mutex to serialize access to accept queues and - * fields associated with multiple sockets. This allows us to - * avoid defining a lock order between listen and accept sockets - * until such time as it proves to be a good idea. - */ -extern struct mtx accept_mtx; -#define ACCEPT_LOCK_ASSERT() mtx_assert(&accept_mtx, MA_OWNED) -#define ACCEPT_UNLOCK_ASSERT() mtx_assert(&accept_mtx, MA_NOTOWNED) -#define ACCEPT_LOCK() mtx_lock(&accept_mtx) -#define ACCEPT_UNLOCK() mtx_unlock(&accept_mtx) - -/* - * Per-socket mutex: we reuse the receive socket buffer mutex for space - * efficiency. This decision should probably be revisited as we optimize - * locking for the socket code. - */ -#define SOCK_MTX(_so) SOCKBUF_MTX(&(_so)->so_rcv) -#define SOCK_LOCK(_so) SOCKBUF_LOCK(&(_so)->so_rcv) -#define SOCK_OWNED(_so) SOCKBUF_OWNED(&(_so)->so_rcv) -#define SOCK_UNLOCK(_so) SOCKBUF_UNLOCK(&(_so)->so_rcv) -#define SOCK_LOCK_ASSERT(_so) SOCKBUF_LOCK_ASSERT(&(_so)->so_rcv) - -/* - * Socket state bits stored in so_qstate. - */ -#define SQ_INCOMP 0x0800 /* unaccepted, incomplete connection */ -#define SQ_COMP 0x1000 /* unaccepted, complete connection */ +#define SOCK_MTX(so) &(so)->so_lock +#define SOCK_LOCK(so) mtx_lock(&(so)->so_lock) +#define SOCK_OWNED(so) mtx_owned(&(so)->so_lock) +#define SOCK_UNLOCK(so) mtx_unlock(&(so)->so_lock) +#define SOCK_LOCK_ASSERT(so) mtx_assert(&(so)->so_lock, MA_OWNED) +#define SOCK_UNLOCK_ASSERT(so) mtx_assert(&(so)->so_lock, MA_NOTOWNED) + +#define SOLISTENING(sol) (((sol)->so_options & SO_ACCEPTCONN) != 0) +#define SOLISTEN_LOCK(sol) do { \ + mtx_lock(&(sol)->so_lock); \ + KASSERT(SOLISTENING(sol), \ + ("%s: %p not listening", __func__, (sol))); \ +} while (0) +#define SOLISTEN_TRYLOCK(sol) mtx_trylock(&(sol)->so_lock) +#define SOLISTEN_UNLOCK(sol) do { \ + KASSERT(SOLISTENING(sol), \ + ("%s: %p not listening", __func__, (sol))); \ + mtx_unlock(&(sol)->so_lock); \ +} while (0) +#define SOLISTEN_LOCK_ASSERT(sol) do { \ + mtx_assert(&(sol)->so_lock, MA_OWNED); \ + KASSERT(SOLISTENING(sol), \ + ("%s: %p not listening", __func__, (sol))); \ +} while (0) /* * Externalized form of struct socket used by the sysctl(3) interface. @@ -212,8 +242,7 @@ struct xsocket { /* can we read something from so? */ #define soreadabledata(so) \ - (sbavail(&(so)->so_rcv) >= (so)->so_rcv.sb_lowat || \ - !TAILQ_EMPTY(&(so)->so_comp) || (so)->so_error) + (sbavail(&(so)->so_rcv) >= (so)->so_rcv.sb_lowat || (so)->so_error) #define soreadable(so) \ (soreadabledata(so) || ((so)->so_rcv.sb_state & SBS_CANTRCVMORE)) @@ -226,26 +255,19 @@ struct xsocket { (so)->so_error) /* - * soref()/sorele() ref-count the socket structure. Note that you must - * still explicitly close the socket, but the last ref count will free - * the structure. + * soref()/sorele() ref-count the socket structure. + * soref() may be called without owning socket lock, but in that case a + * caller must own something that holds socket, and so_count must be not 0. + * Note that you must still explicitly close the socket, but the last ref + * count will free the structure. */ -#define soref(so) do { \ - SOCK_LOCK_ASSERT(so); \ - ++(so)->so_count; \ -} while (0) - +#define soref(so) refcount_acquire(&(so)->so_count) #define sorele(so) do { \ - ACCEPT_LOCK_ASSERT(); \ SOCK_LOCK_ASSERT(so); \ - if ((so)->so_count <= 0) \ - panic("sorele"); \ - if (--(so)->so_count == 0) \ + if (refcount_release(&(so)->so_count)) \ sofree(so); \ - else { \ + else \ SOCK_UNLOCK(so); \ - ACCEPT_UNLOCK(); \ - } \ } while (0) /* @@ -370,10 +392,11 @@ void sohasoutofband(struct socket *so); int solisten(struct socket *so, int backlog, struct thread *td); void solisten_proto(struct socket *so, int backlog); int solisten_proto_check(struct socket *so); +int solisten_dequeue(struct socket *, struct socket **, int); struct socket * sonewconn(struct socket *head, int connstatus); - - +struct socket * + sopeeloff(struct socket *); int sopoll(struct socket *so, int events, struct ucred *active_cred, struct thread *td); int sopoll_generic(struct socket *so, int events, @@ -402,15 +425,16 @@ int sosend_generic(struct socket *so, struct sockaddr *addr, int flags, struct thread *td); int soshutdown(struct socket *so, int how); void sotoxsocket(struct socket *so, struct xsocket *xso); -void soupcall_clear(struct socket *so, int which); -void soupcall_set(struct socket *so, int which, - int (*func)(struct socket *, void *, int), void *arg); +void soupcall_clear(struct socket *, int); +void soupcall_set(struct socket *, int, so_upcall_t, void *); +void solisten_upcall_set(struct socket *, so_upcall_t, void *); void sowakeup(struct socket *so, struct sockbuf *sb); #ifndef __rtems__ void sowakeup_aio(struct socket *so, struct sockbuf *sb); #else /* __rtems__ */ #define sowakeup_aio(so, sb) (void)0 #endif /* __rtems__ */ +void solisten_wakeup(struct socket *); int selsocket(struct socket *so, int events, struct timeval *tv, struct thread *td); diff --git a/freebsd/sys/sys/sockopt.h b/freebsd/sys/sys/sockopt.h index 4131a5b7..029e5b4a 100644 --- a/freebsd/sys/sys/sockopt.h +++ b/freebsd/sys/sys/sockopt.h @@ -64,8 +64,8 @@ int sooptcopyout(struct sockopt *sopt, const void *buf, size_t len); int soopt_getm(struct sockopt *sopt, struct mbuf **mp); int soopt_mcopyin(struct sockopt *sopt, struct mbuf *m); int soopt_mcopyout(struct sockopt *sopt, struct mbuf *m); -int do_getopt_accept_filter(struct socket *so, struct sockopt *sopt); -int do_setopt_accept_filter(struct socket *so, struct sockopt *sopt); +int accept_filt_getopt(struct socket *, struct sockopt *); +int accept_filt_setopt(struct socket *, struct sockopt *); int so_setsockopt(struct socket *so, int level, int optname, void *optval, size_t optlen); diff --git a/freebsd/sys/sys/sysproto.h b/freebsd/sys/sys/sysproto.h index 151b380b..d5cda835 100644 --- a/freebsd/sys/sys/sysproto.h +++ b/freebsd/sys/sys/sysproto.h @@ -1002,18 +1002,6 @@ struct getresgid_args { struct kqueue_args { register_t dummy; }; -struct kevent_args { - char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)]; -#ifndef __rtems__ - char changelist_l_[PADL_(struct kevent *)]; struct kevent * changelist; char changelist_r_[PADR_(struct kevent *)]; -#else /* __rtems__ */ - char changelist_l_[PADL_(struct kevent *)]; const struct kevent * changelist; char changelist_r_[PADR_(struct kevent *)]; -#endif /* __rtems__ */ - char nchanges_l_[PADL_(int)]; int nchanges; char nchanges_r_[PADR_(int)]; - char eventlist_l_[PADL_(struct kevent *)]; struct kevent * eventlist; char eventlist_r_[PADR_(struct kevent *)]; - char nevents_l_[PADL_(int)]; int nevents; char nevents_r_[PADR_(int)]; - char timeout_l_[PADL_(const struct timespec *)]; const struct timespec * timeout; char timeout_r_[PADR_(const struct timespec *)]; -}; #ifndef __rtems__ struct extattr_set_fd_args { char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)]; @@ -1806,6 +1794,20 @@ struct mknodat_args { char mode_l_[PADL_(mode_t)]; mode_t mode; char mode_r_[PADR_(mode_t)]; char dev_l_[PADL_(dev_t)]; dev_t dev; char dev_r_[PADR_(dev_t)]; }; +#endif /* __rtems__ */ +struct kevent_args { + char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)]; +#ifndef __rtems__ + char changelist_l_[PADL_(struct kevent *)]; struct kevent * changelist; char changelist_r_[PADR_(struct kevent *)]; +#else /* __rtems__ */ + char changelist_l_[PADL_(struct kevent *)]; const struct kevent * changelist; char changelist_r_[PADR_(struct kevent *)]; +#endif /* __rtems__ */ + char nchanges_l_[PADL_(int)]; int nchanges; char nchanges_r_[PADR_(int)]; + char eventlist_l_[PADL_(struct kevent *)]; struct kevent * eventlist; char eventlist_r_[PADR_(struct kevent *)]; + char nevents_l_[PADL_(int)]; int nevents; char nevents_r_[PADR_(int)]; + char timeout_l_[PADL_(const struct timespec *)]; const struct timespec * timeout; char timeout_r_[PADR_(const struct timespec *)]; +}; +#ifndef __rtems__ int nosys(struct thread *, struct nosys_args *); void sys_sys_exit(struct thread *, struct sys_exit_args *); int sys_fork(struct thread *, struct fork_args *); @@ -2021,7 +2023,6 @@ int sys_aio_waitcomplete(struct thread *, struct aio_waitcomplete_args *); int sys_getresuid(struct thread *, struct getresuid_args *); int sys_getresgid(struct thread *, struct getresgid_args *); int sys_kqueue(struct thread *, struct kqueue_args *); -int sys_kevent(struct thread *, struct kevent_args *); int sys_extattr_set_fd(struct thread *, struct extattr_set_fd_args *); int sys_extattr_get_fd(struct thread *, struct extattr_get_fd_args *); int sys_extattr_delete_fd(struct thread *, struct extattr_delete_fd_args *); @@ -2186,6 +2187,7 @@ int sys_fstatfs(struct thread *, struct fstatfs_args *); int sys_getfsstat(struct thread *, struct getfsstat_args *); int sys_fhstatfs(struct thread *, struct fhstatfs_args *); int sys_mknodat(struct thread *, struct mknodat_args *); +int sys_kevent(struct thread *, struct kevent_args *); #ifdef COMPAT_43 @@ -2564,6 +2566,14 @@ struct freebsd11_fhstat_args { char u_fhp_l_[PADL_(const struct fhandle *)]; const struct fhandle * u_fhp; char u_fhp_r_[PADR_(const struct fhandle *)]; char sb_l_[PADL_(struct freebsd11_stat *)]; struct freebsd11_stat * sb; char sb_r_[PADR_(struct freebsd11_stat *)]; }; +struct freebsd11_kevent_args { + char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)]; + char changelist_l_[PADL_(struct kevent_freebsd11 *)]; struct kevent_freebsd11 * changelist; char changelist_r_[PADR_(struct kevent_freebsd11 *)]; + char nchanges_l_[PADL_(int)]; int nchanges; char nchanges_r_[PADR_(int)]; + char eventlist_l_[PADL_(struct kevent_freebsd11 *)]; struct kevent_freebsd11 * eventlist; char eventlist_r_[PADR_(struct kevent_freebsd11 *)]; + char nevents_l_[PADL_(int)]; int nevents; char nevents_r_[PADR_(int)]; + char timeout_l_[PADL_(const struct timespec *)]; const struct timespec * timeout; char timeout_r_[PADR_(const struct timespec *)]; +}; struct freebsd11_getfsstat_args { char buf_l_[PADL_(struct freebsd11_statfs *)]; struct freebsd11_statfs * buf; char buf_r_[PADR_(struct freebsd11_statfs *)]; char bufsize_l_[PADL_(long)]; long bufsize; char bufsize_r_[PADR_(long)]; @@ -2603,6 +2613,7 @@ int freebsd11_nstat(struct thread *, struct freebsd11_nstat_args *); int freebsd11_nfstat(struct thread *, struct freebsd11_nfstat_args *); int freebsd11_nlstat(struct thread *, struct freebsd11_nlstat_args *); int freebsd11_fhstat(struct thread *, struct freebsd11_fhstat_args *); +int freebsd11_kevent(struct thread *, struct freebsd11_kevent_args *); int freebsd11_getfsstat(struct thread *, struct freebsd11_getfsstat_args *); int freebsd11_statfs(struct thread *, struct freebsd11_statfs_args *); int freebsd11_fstatfs(struct thread *, struct freebsd11_fstatfs_args *); @@ -2897,7 +2908,7 @@ int freebsd11_mknodat(struct thread *, struct freebsd11_mknodat_args *); #define SYS_AUE_getresuid AUE_GETRESUID #define SYS_AUE_getresgid AUE_GETRESGID #define SYS_AUE_kqueue AUE_KQUEUE -#define SYS_AUE_kevent AUE_KEVENT +#define SYS_AUE_freebsd11_kevent AUE_KEVENT #define SYS_AUE_extattr_set_fd AUE_EXTATTR_SET_FD #define SYS_AUE_extattr_get_fd AUE_EXTATTR_GET_FD #define SYS_AUE_extattr_delete_fd AUE_EXTATTR_DELETE_FD @@ -3068,6 +3079,7 @@ int freebsd11_mknodat(struct thread *, struct freebsd11_mknodat_args *); #define SYS_AUE_getfsstat AUE_GETFSSTAT #define SYS_AUE_fhstatfs AUE_FHSTATFS #define SYS_AUE_mknodat AUE_MKNODAT +#define SYS_AUE_kevent AUE_KEVENT #endif /* __rtems__ */ #undef PAD_ diff --git a/freebsd/sys/sys/systm.h b/freebsd/sys/sys/systm.h index 6b557c56..4488c7cd 100644 --- a/freebsd/sys/sys/systm.h +++ b/freebsd/sys/sys/systm.h @@ -102,7 +102,7 @@ extern int vm_guest; /* Running as virtual machine guest? */ * Keep in sync with vm_guest_sysctl_names[]. */ enum VM_GUEST { VM_GUEST_NO = 0, VM_GUEST_VM, VM_GUEST_XEN, VM_GUEST_HV, - VM_GUEST_VMWARE, VM_GUEST_KVM, VM_LAST }; + VM_GUEST_VMWARE, VM_GUEST_KVM, VM_GUEST_BHYVE, VM_LAST }; #if defined(WITNESS) || defined(INVARIANT_SUPPORT) #ifndef __rtems__ diff --git a/freebsd/sys/sys/taskqueue.h b/freebsd/sys/sys/taskqueue.h index 583f796e..7db75c6d 100644 --- a/freebsd/sys/sys/taskqueue.h +++ b/freebsd/sys/sys/taskqueue.h @@ -79,6 +79,9 @@ int taskqueue_start_threads_cpuset(struct taskqueue **tqp, int count, int taskqueue_enqueue(struct taskqueue *queue, struct task *task); int taskqueue_enqueue_timeout(struct taskqueue *queue, struct timeout_task *timeout_task, int ticks); +int taskqueue_enqueue_timeout_sbt(struct taskqueue *queue, + struct timeout_task *timeout_task, sbintime_t sbt, sbintime_t pr, + int flags); int taskqueue_poll_is_busy(struct taskqueue *queue, struct task *task); int taskqueue_cancel(struct taskqueue *queue, struct task *task, u_int *pendp); diff --git a/freebsd/sys/sys/unpcb.h b/freebsd/sys/sys/unpcb.h index 619b68dd..76bc63a9 100644 --- a/freebsd/sys/sys/unpcb.h +++ b/freebsd/sys/sys/unpcb.h @@ -96,14 +96,8 @@ struct unpcb { * and is really the credentials of the connected peer. This is used * to determine whether the contents should be sent to the user or * not. - * - * UNP_HAVEPCCACHED - indicates that the unp_peercred member is filled - * in, but does *not* contain the credentials of the connected peer - * (there may not even be a peer). This is set in unp_listen() when - * it fills in unp_peercred for later consumption by unp_connect(). */ #define UNP_HAVEPC 0x001 -#define UNP_HAVEPCCACHED 0x002 #define UNP_WANTCRED 0x004 /* credentials wanted */ #define UNP_CONNWAIT 0x008 /* connect blocks until accepted */ diff --git a/freebsd/sys/sys/uuid.h b/freebsd/sys/sys/uuid.h index 0748f611..97971fd7 100644 --- a/freebsd/sys/sys/uuid.h +++ b/freebsd/sys/sys/uuid.h @@ -65,6 +65,7 @@ int snprintf_uuid(char *, size_t, struct uuid *); int printf_uuid(struct uuid *); int sbuf_printf_uuid(struct sbuf *, struct uuid *); int parse_uuid(const char *, struct uuid *); +int uuidcmp(const struct uuid *, const struct uuid *); void be_uuid_dec(void const *buf, struct uuid *uuid); void be_uuid_enc(void *buf, struct uuid const *uuid); diff --git a/freebsd/sys/sys/vnode.h b/freebsd/sys/sys/vnode.h index 8f123d5e..61cbf000 100644 --- a/freebsd/sys/sys/vnode.h +++ b/freebsd/sys/sys/vnode.h @@ -113,14 +113,13 @@ struct vnode { /* * Type specific fields, only one applies to any given vnode. - * See #defines below for renaming to v_* namespace. */ union { - struct mount *vu_mount; /* v ptr to mountpoint (VDIR) */ - struct socket *vu_socket; /* v unix domain net (VSOCK) */ - struct cdev *vu_cdev; /* v device (VCHR, VBLK) */ - struct fifoinfo *vu_fifoinfo; /* v fifo (VFIFO) */ - } v_un; + struct mount *v_mountedhere; /* v ptr to mountpoint (VDIR) */ + struct unpcb *v_unpcb; /* v unix domain net (VSOCK) */ + struct cdev *v_rdev; /* v device (VCHR, VBLK) */ + struct fifoinfo *v_fifoinfo; /* v fifo (VFIFO) */ + }; /* * vfs_hash: (mount + inode) -> vnode hash. The hash value @@ -176,11 +175,6 @@ struct vnode { #endif /* defined(_KERNEL) || defined(_KVM_VNODE) */ -#define v_mountedhere v_un.vu_mount -#define v_socket v_un.vu_socket -#define v_rdev v_un.vu_cdev -#define v_fifoinfo v_un.vu_fifoinfo - #define bo2vnode(bo) __containerof((bo), struct vnode, v_bufobj) /* XXX: These are temporary to avoid a source sweep at this time */ @@ -201,7 +195,7 @@ struct xvnode { long xv_numoutput; /* num of writes in progress */ enum vtype xv_type; /* vnode type */ union { - void *xvu_socket; /* socket, if VSOCK */ + void *xvu_socket; /* unpcb, if VSOCK */ void *xvu_fifo; /* fifo, if VFIFO */ dev_t xvu_rdev; /* maj/min, if VBLK/VCHR */ struct { @@ -617,7 +611,7 @@ void cache_purgevfs(struct mount *mp, bool force); int change_dir(struct vnode *vp, struct thread *td); void cvtstat(struct stat *st, struct ostat *ost); void freebsd11_cvtnstat(struct stat *sb, struct nstat *nsb); -void freebsd11_cvtstat(struct stat *st, struct freebsd11_stat *ost); +int freebsd11_cvtstat(struct stat *st, struct freebsd11_stat *ost); int getnewvnode(const char *tag, struct mount *mp, struct vop_vector *vops, struct vnode **vpp); void getnewvnode_reserve(u_int count); diff --git a/freebsd/sys/vm/uma_core.c b/freebsd/sys/vm/uma_core.c index c69faea9..011f6494 100644 --- a/freebsd/sys/vm/uma_core.c +++ b/freebsd/sys/vm/uma_core.c @@ -52,13 +52,6 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -/* I should really use ktr.. */ -/* -#define UMA_DEBUG 1 -#define UMA_DEBUG_ALLOC 1 -#define UMA_DEBUG_ALLOC_1 1 -*/ - #include <rtems/bsd/local/opt_ddb.h> #include <rtems/bsd/local/opt_param.h> #include <rtems/bsd/local/opt_vm.h> @@ -162,12 +155,15 @@ static LIST_HEAD(,uma_zone) uma_cachezones = static struct rwlock_padalign uma_rwlock; #ifndef __rtems__ -/* Linked list of boot time pages */ -static LIST_HEAD(,uma_slab) uma_boot_pages = - LIST_HEAD_INITIALIZER(uma_boot_pages); - -/* This mutex protects the boot time pages list */ -static struct mtx_padalign uma_boot_pages_mtx; +/* + * Pointer and counter to pool of pages, that is preallocated at + * startup to bootstrap UMA. Early zones continue to use the pool + * until it is depleted, so allocations may happen after boot, thus + * we need a mutex to protect it. + */ +static char *bootmem; +static int boot_pages; +static struct mtx uma_boot_pages_mtx; #endif /* __rtems__ */ static struct sx uma_drain_lock; @@ -862,6 +858,9 @@ keg_free_slab(uma_keg_t keg, uma_slab_t slab, int start) int i; uint8_t flags; + CTR4(KTR_UMA, "keg_free_slab keg %s(%p) slab %p, returning %d bytes", + keg->uk_name, keg, slab, PAGE_SIZE * keg->uk_ppera); + mem = slab->us_data; flags = slab->us_flags; i = start; @@ -872,10 +871,6 @@ keg_free_slab(uma_keg_t keg, uma_slab_t slab, int start) } if (keg->uk_flags & UMA_ZONE_OFFPAGE) zone_free_item(keg->uk_slabzone, slab, NULL, SKIP_NONE); -#ifdef UMA_DEBUG - printf("%s: Returning %d bytes.\n", keg->uk_name, - PAGE_SIZE * keg->uk_ppera); -#endif keg->uk_freef(mem, PAGE_SIZE * keg->uk_ppera, flags); } @@ -898,9 +893,8 @@ keg_drain(uma_keg_t keg) if (keg->uk_flags & UMA_ZONE_NOFREE || keg->uk_freef == NULL) return; -#ifdef UMA_DEBUG - printf("%s free items: %u\n", keg->uk_name, keg->uk_free); -#endif + CTR3(KTR_UMA, "keg_drain %s(%p) free items: %u", + keg->uk_name, keg, keg->uk_free); KEG_LOCK(keg); if (keg->uk_free == 0) goto finished; @@ -992,9 +986,6 @@ keg_alloc_slab(uma_keg_t keg, uma_zone_t zone, int wait) slab = NULL; mem = NULL; -#ifdef UMA_DEBUG - printf("alloc_slab: Allocating a new slab for %s\n", keg->uk_name); -#endif allocf = keg->uk_allocf; KEG_UNLOCK(keg); @@ -1059,6 +1050,9 @@ keg_alloc_slab(uma_keg_t keg, uma_zone_t zone, int wait) out: KEG_LOCK(keg); + CTR3(KTR_UMA, "keg_alloc_slab: allocated slab %p for %s(%p)", + slab, keg->uk_name, keg); + if (slab != NULL) { if (keg->uk_flags & UMA_ZONE_HASH) UMA_HASH_INSERT(&keg->uk_hash, slab, mem); @@ -1080,36 +1074,24 @@ static void * startup_alloc(uma_zone_t zone, vm_size_t bytes, uint8_t *pflag, int wait) { uma_keg_t keg; - uma_slab_t tmps; - int pages, check_pages; + void *mem; + int pages; keg = zone_first_keg(zone); pages = howmany(bytes, PAGE_SIZE); - check_pages = pages - 1; KASSERT(pages > 0, ("startup_alloc can't reserve 0 pages\n")); /* * Check our small startup cache to see if it has pages remaining. */ mtx_lock(&uma_boot_pages_mtx); - - /* First check if we have enough room. */ - tmps = LIST_FIRST(&uma_boot_pages); - while (tmps != NULL && check_pages-- > 0) - tmps = LIST_NEXT(tmps, us_link); - if (tmps != NULL) { - /* - * It's ok to lose tmps references. The last one will - * have tmps->us_data pointing to the start address of - * "pages" contiguous pages of memory. - */ - while (pages-- > 0) { - tmps = LIST_FIRST(&uma_boot_pages); - LIST_REMOVE(tmps, us_link); - } + if (pages <= boot_pages) { + mem = bootmem; + boot_pages -= pages; + bootmem += pages * PAGE_SIZE; mtx_unlock(&uma_boot_pages_mtx); - *pflag = tmps->us_flags; - return (tmps->us_data); + *pflag = UMA_SLAB_BOOT; + return (mem); } mtx_unlock(&uma_boot_pages_mtx); if (booted < UMA_STARTUP2) @@ -1344,15 +1326,13 @@ keg_small_init(uma_keg_t keg) keg->uk_ipers = slabsize / keg->uk_rsize; KASSERT(keg->uk_ipers > 0 && keg->uk_ipers <= SLAB_SETSIZE, ("%s: keg->uk_ipers %u", __func__, keg->uk_ipers)); -#ifdef UMA_DEBUG - printf("UMA decided we need offpage slab headers for " - "keg: %s, calculated wastedspace = %d, " + CTR6(KTR_UMA, "UMA decided we need offpage slab headers for " + "keg: %s(%p), calculated wastedspace = %d, " "maximum wasted space allowed = %d, " "calculated ipers = %d, " - "new wasted space = %d\n", keg->uk_name, wastedspace, + "new wasted space = %d\n", keg->uk_name, keg, wastedspace, slabsize / UMA_MAX_WASTE, keg->uk_ipers, slabsize - keg->uk_ipers * keg->uk_rsize); -#endif keg->uk_flags |= UMA_ZONE_OFFPAGE; } @@ -1465,8 +1445,6 @@ keg_ctor(void *mem, int size, void *udata, int flags) keg->uk_reserve = 0; keg->uk_pages = 0; keg->uk_flags = arg->flags; - keg->uk_allocf = page_alloc; - keg->uk_freef = page_free; keg->uk_slabzone = NULL; /* @@ -1503,32 +1481,28 @@ keg_ctor(void *mem, int size, void *udata, int flags) if (keg->uk_flags & UMA_ZONE_OFFPAGE) keg->uk_slabzone = slabzone; +#ifndef __rtems__ /* * If we haven't booted yet we need allocations to go through the * startup cache until the vm is ready. */ - if (keg->uk_ppera == 1) { + if (booted < UMA_STARTUP2) + keg->uk_allocf = startup_alloc; #ifdef UMA_MD_SMALL_ALLOC + else if (keg->uk_ppera == 1) keg->uk_allocf = uma_small_alloc; - keg->uk_freef = uma_small_free; - -#ifndef __rtems__ - if (booted < UMA_STARTUP) - keg->uk_allocf = startup_alloc; +#endif + else #endif /* __rtems__ */ -#else + keg->uk_allocf = page_alloc; #ifndef __rtems__ - if (booted < UMA_STARTUP2) - keg->uk_allocf = startup_alloc; -#endif /* __rtems__ */ +#ifdef UMA_MD_SMALL_ALLOC + if (keg->uk_ppera == 1) + keg->uk_freef = uma_small_free; + else #endif -#ifndef __rtems__ - } else if (booted < UMA_STARTUP2 && - (keg->uk_flags & UMA_ZFLAG_INTERNAL)) - keg->uk_allocf = startup_alloc; -#else /* __rtems__ */ - } #endif /* __rtems__ */ + keg->uk_freef = page_free; /* * Initialize keg's lock @@ -1570,13 +1544,10 @@ keg_ctor(void *mem, int size, void *udata, int flags) if (keg->uk_flags & UMA_ZONE_HASH) hash_alloc(&keg->uk_hash); -#ifdef UMA_DEBUG - printf("UMA: %s(%p) size %d(%d) flags %#x ipers %d ppera %d out %d free %d\n", - zone->uz_name, zone, keg->uk_size, keg->uk_rsize, keg->uk_flags, - keg->uk_ipers, keg->uk_ppera, + CTR5(KTR_UMA, "keg_ctor %p zone %s(%p) out %d free %d\n", + keg, zone->uz_name, zone, (keg->uk_pages / keg->uk_ppera) * keg->uk_ipers - keg->uk_free, keg->uk_free); -#endif LIST_INSERT_HEAD(&keg->uk_zones, zone, uz_link); @@ -1817,17 +1788,10 @@ zone_foreach(void (*zfunc)(uma_zone_t)) /* Public functions */ /* See uma.h */ void -uma_startup(void *bootmem, int boot_pages) +uma_startup(void *mem, int npages) { struct uma_zctor_args args; -#ifndef __rtems__ - uma_slab_t slab; - int i; -#endif /* __rtems__ */ -#ifdef UMA_DEBUG - printf("Creating uma keg headers zone and keg.\n"); -#endif rw_init(&uma_rwlock, "UMA lock"); /* "manually" create the initial zone */ @@ -1845,21 +1809,11 @@ uma_startup(void *bootmem, int boot_pages) zone_ctor(kegs, sizeof(struct uma_zone), &args, M_WAITOK); #ifndef __rtems__ -#ifdef UMA_DEBUG - printf("Filling boot free list.\n"); -#endif - for (i = 0; i < boot_pages; i++) { - slab = (uma_slab_t)((uint8_t *)bootmem + (i * UMA_SLAB_SIZE)); - slab->us_data = (uint8_t *)slab; - slab->us_flags = UMA_SLAB_BOOT; - LIST_INSERT_HEAD(&uma_boot_pages, slab, us_link); - } mtx_init(&uma_boot_pages_mtx, "UMA boot pages", NULL, MTX_DEF); + bootmem = mem; + boot_pages = npages; #endif /* __rtems__ */ -#ifdef UMA_DEBUG - printf("Creating uma zone headers zone and keg.\n"); -#endif args.name = "UMA Zones"; args.size = sizeof(struct uma_zone) + (sizeof(struct uma_cache) * (mp_maxid + 1)); @@ -1873,10 +1827,6 @@ uma_startup(void *bootmem, int boot_pages) /* The initial zone has no Per cpu queues so it's smaller */ zone_ctor(zones, sizeof(struct uma_zone), &args, M_WAITOK); -#ifdef UMA_DEBUG - printf("Creating slab and hash zones.\n"); -#endif - /* Now make a zone for slab headers */ slabzone = uma_zcreate("UMA Slabs", sizeof(struct uma_slab), @@ -1893,10 +1843,6 @@ uma_startup(void *bootmem, int boot_pages) #ifndef __rtems__ booted = UMA_STARTUP; #endif /* __rtems__ */ - -#ifdef UMA_DEBUG - printf("UMA startup complete.\n"); -#endif } #ifdef __rtems__ static void @@ -1920,9 +1866,6 @@ uma_startup2(void) booted = UMA_STARTUP2; bucket_enable(); sx_init(&uma_drain_lock, "umadrain"); -#ifdef UMA_DEBUG - printf("UMA startup2 complete.\n"); -#endif } #endif /* __rtems__ */ @@ -1934,14 +1877,9 @@ uma_startup2(void) static void uma_startup3(void) { -#ifdef UMA_DEBUG - printf("Starting callout.\n"); -#endif + callout_init(&uma_callout, 1); callout_reset(&uma_callout, UMA_TIMEOUT * hz, uma_timeout, NULL); -#ifdef UMA_DEBUG - printf("UMA startup3 complete.\n"); -#endif } static uma_keg_t @@ -2199,11 +2137,8 @@ uma_zalloc_arg(uma_zone_t zone, void *udata, int flags) random_harvest_fast_uma(&zone, sizeof(zone), 1, RANDOM_UMA); /* This is the fast path allocation */ -#ifdef UMA_DEBUG_ALLOC_1 - printf("Allocating one item from %s(%p)\n", zone->uz_name, zone); -#endif - CTR3(KTR_UMA, "uma_zalloc_arg thread %x zone %s flags %d", curthread, - zone->uz_name, flags); + CTR4(KTR_UMA, "uma_zalloc_arg thread %x zone %s(%p) flags %d", + curthread, zone->uz_name, zone, flags); if (flags & M_WAITOK) { WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, @@ -2278,9 +2213,9 @@ zalloc_start: */ bucket = cache->uc_freebucket; if (bucket != NULL && bucket->ub_cnt > 0) { -#ifdef UMA_DEBUG_ALLOC - printf("uma_zalloc: Swapping empty with alloc.\n"); -#endif + CTR2(KTR_UMA, + "uma_zalloc: zone %s(%p) swapping empty with alloc", + zone->uz_name, zone); cache->uc_freebucket = cache->uc_allocbucket; cache->uc_allocbucket = bucket; goto zalloc_start; @@ -2361,6 +2296,8 @@ zalloc_start: * will use the just filled bucket. */ bucket = zone_alloc_bucket(zone, udata, flags); + CTR3(KTR_UMA, "uma_zalloc: zone %s(%p) bucket zone returned %p", + zone->uz_name, zone, bucket); if (bucket != NULL) { ZONE_LOCK(zone); critical_enter(); @@ -2382,10 +2319,6 @@ zalloc_start: /* * We may not be able to get a bucket so return an actual item. */ -#ifdef UMA_DEBUG - printf("uma_zalloc_arg: Bucketzone returned NULL\n"); -#endif - zalloc_item: item = zone_alloc_item(zone, udata, flags); @@ -2693,9 +2626,6 @@ zone_alloc_item(uma_zone_t zone, void *udata, int flags) item = NULL; -#ifdef UMA_DEBUG_ALLOC - printf("INTERNAL: Allocating one item from %s(%p)\n", zone->uz_name, zone); -#endif if (zone->uz_import(zone->uz_arg, &item, 1, flags) != 1) goto fail; atomic_add_long(&zone->uz_allocs, 1); @@ -2724,9 +2654,14 @@ zone_alloc_item(uma_zone_t zone, void *udata, int flags) if (flags & M_ZERO) uma_zero_item(item, zone); + CTR3(KTR_UMA, "zone_alloc_item item %p from %s(%p)", item, + zone->uz_name, zone); + return (item); fail: + CTR2(KTR_UMA, "zone_alloc_item failed from %s(%p)", + zone->uz_name, zone); atomic_add_long(&zone->uz_fails, 1); return (NULL); } @@ -2743,9 +2678,6 @@ uma_zfree_arg(uma_zone_t zone, void *item, void *udata) /* Enable entropy collection for RANDOM_ENABLE_UMA kernel option */ random_harvest_fast_uma(&zone, sizeof(zone), 1, RANDOM_UMA); -#ifdef UMA_DEBUG_ALLOC_1 - printf("Freeing item %p to %s(%p)\n", item, zone->uz_name, zone); -#endif CTR2(KTR_UMA, "uma_zfree_arg thread %x zone %s", curthread, zone->uz_name); @@ -2859,9 +2791,9 @@ zfree_start: /* Can we throw this on the zone full list? */ if (bucket != NULL) { -#ifdef UMA_DEBUG_ALLOC - printf("uma_zfree: Putting old bucket on the free list.\n"); -#endif + CTR3(KTR_UMA, + "uma_zfree: zone %s(%p) putting bucket %p on free list", + zone->uz_name, zone, bucket); /* ub_cnt is pointing to the last free item */ KASSERT(bucket->ub_cnt != 0, ("uma_zfree: Attempting to insert an empty bucket onto the full list.\n")); @@ -2876,10 +2808,9 @@ zfree_start: zone->uz_count++; ZONE_UNLOCK(zone); -#ifdef UMA_DEBUG_ALLOC - printf("uma_zfree: Allocating new free bucket.\n"); -#endif bucket = bucket_alloc(zone, udata, M_NOWAIT); + CTR3(KTR_UMA, "uma_zfree: zone %s(%p) allocated bucket %p", + zone->uz_name, zone, bucket); if (bucket) { critical_enter(); cpu = curcpu; @@ -3281,9 +3212,7 @@ static void uma_reclaim_locked(bool kmem_danger) { -#ifdef UMA_DEBUG - printf("UMA: vm asked us to release pages!\n"); -#endif + CTR0(KTR_UMA, "UMA: vm asked us to release pages!"); sx_assert(&uma_drain_lock, SA_XLOCKED); bucket_enable(); zone_foreach(zone_drain); diff --git a/freebsd/sys/vm/vm.h b/freebsd/sys/vm/vm.h index a41bc0b1..d0945a3d 100644 --- a/freebsd/sys/vm/vm.h +++ b/freebsd/sys/vm/vm.h @@ -78,6 +78,7 @@ typedef u_char vm_prot_t; /* protection codes */ #define VM_PROT_WRITE ((vm_prot_t) 0x02) #define VM_PROT_EXECUTE ((vm_prot_t) 0x04) #define VM_PROT_COPY ((vm_prot_t) 0x08) /* copy-on-read */ +#define VM_PROT_FAULT_LOOKUP ((vm_prot_t) 0x010) #define VM_PROT_ALL (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE) #define VM_PROT_RW (VM_PROT_READ|VM_PROT_WRITE) |