Index: projects/ifnet/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc =================================================================== --- projects/ifnet/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc (revision 277209) +++ projects/ifnet/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc (revision 277210) @@ -1,1189 +1,1188 @@ //===-- sanitizer_platform_limits_posix.cc --------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of Sanitizer common code. // // Sizes and layouts of platform-specific POSIX data structures. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC // Tests in this file assume that off_t-dependent data structures match the // libc ABI. For example, struct dirent here is what readdir() function (as // exported from libc) returns, and not the user-facing "dirent", which // depends on _FILE_OFFSET_BITS setting. // To get this "true" dirent definition, we undefine _FILE_OFFSET_BITS below. #ifdef _FILE_OFFSET_BITS #undef _FILE_OFFSET_BITS #endif #if SANITIZER_FREEBSD #define _WANT_RTENTRY #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !SANITIZER_ANDROID #include #include #endif #if SANITIZER_LINUX #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif #if SANITIZER_FREEBSD # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include -# include # include # include # include # include #define _KERNEL // to declare 'shminfo' structure # include #undef _KERNEL #undef INLINE // to avoid clashes with sanitizers' definitions #endif #if SANITIZER_FREEBSD || SANITIZER_IOS #undef IOC_DIRMASK #endif #if SANITIZER_LINUX || SANITIZER_FREEBSD # include # include #endif #if !SANITIZER_ANDROID #include #include #include #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif // SANITIZER_LINUX && !SANITIZER_ANDROID #if SANITIZER_ANDROID #include #include #include #include #endif #if SANITIZER_LINUX #include #include #include #include #endif // SANITIZER_LINUX #if SANITIZER_MAC #include #include #include #endif // Include these after system headers to avoid name clashes and ambiguities. #include "sanitizer_internal_defs.h" #include "sanitizer_platform_limits_posix.h" namespace __sanitizer { unsigned struct_utsname_sz = sizeof(struct utsname); unsigned struct_stat_sz = sizeof(struct stat); #if !SANITIZER_IOS && !SANITIZER_FREEBSD unsigned struct_stat64_sz = sizeof(struct stat64); #endif // !SANITIZER_IOS && !SANITIZER_FREEBSD unsigned struct_rusage_sz = sizeof(struct rusage); unsigned struct_tm_sz = sizeof(struct tm); unsigned struct_passwd_sz = sizeof(struct passwd); unsigned struct_group_sz = sizeof(struct group); unsigned siginfo_t_sz = sizeof(siginfo_t); unsigned struct_sigaction_sz = sizeof(struct sigaction); unsigned struct_itimerval_sz = sizeof(struct itimerval); unsigned pthread_t_sz = sizeof(pthread_t); unsigned pthread_cond_t_sz = sizeof(pthread_cond_t); unsigned pid_t_sz = sizeof(pid_t); unsigned timeval_sz = sizeof(timeval); unsigned uid_t_sz = sizeof(uid_t); unsigned gid_t_sz = sizeof(gid_t); unsigned mbstate_t_sz = sizeof(mbstate_t); unsigned sigset_t_sz = sizeof(sigset_t); unsigned struct_timezone_sz = sizeof(struct timezone); unsigned struct_tms_sz = sizeof(struct tms); unsigned struct_sigevent_sz = sizeof(struct sigevent); unsigned struct_sched_param_sz = sizeof(struct sched_param); #if SANITIZER_MAC && !SANITIZER_IOS unsigned struct_statfs64_sz = sizeof(struct statfs64); #endif // SANITIZER_MAC && !SANITIZER_IOS #if !SANITIZER_ANDROID unsigned struct_statfs_sz = sizeof(struct statfs); unsigned struct_sockaddr_sz = sizeof(struct sockaddr); unsigned ucontext_t_sz = sizeof(ucontext_t); #endif // !SANITIZER_ANDROID #if SANITIZER_LINUX unsigned struct_epoll_event_sz = sizeof(struct epoll_event); unsigned struct_sysinfo_sz = sizeof(struct sysinfo); unsigned __user_cap_header_struct_sz = sizeof(struct __user_cap_header_struct); unsigned __user_cap_data_struct_sz = sizeof(struct __user_cap_data_struct); unsigned struct_new_utsname_sz = sizeof(struct new_utsname); unsigned struct_old_utsname_sz = sizeof(struct old_utsname); unsigned struct_oldold_utsname_sz = sizeof(struct oldold_utsname); #endif // SANITIZER_LINUX #if SANITIZER_LINUX || SANITIZER_FREEBSD unsigned struct_rlimit_sz = sizeof(struct rlimit); unsigned struct_timespec_sz = sizeof(struct timespec); unsigned struct_utimbuf_sz = sizeof(struct utimbuf); unsigned struct_itimerspec_sz = sizeof(struct itimerspec); #endif // SANITIZER_LINUX || SANITIZER_FREEBSD #if SANITIZER_LINUX && !SANITIZER_ANDROID unsigned struct_ustat_sz = sizeof(struct ustat); unsigned struct_rlimit64_sz = sizeof(struct rlimit64); unsigned struct_statvfs64_sz = sizeof(struct statvfs64); #endif // SANITIZER_LINUX && !SANITIZER_ANDROID #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID unsigned struct_timex_sz = sizeof(struct timex); unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds); unsigned struct_mq_attr_sz = sizeof(struct mq_attr); unsigned struct_statvfs_sz = sizeof(struct statvfs); #endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID uptr sig_ign = (uptr)SIG_IGN; uptr sig_dfl = (uptr)SIG_DFL; uptr sa_siginfo = (uptr)SA_SIGINFO; #if SANITIZER_LINUX int e_tabsz = (int)E_TABSZ; #endif #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID unsigned struct_shminfo_sz = sizeof(struct shminfo); unsigned struct_shm_info_sz = sizeof(struct shm_info); int shmctl_ipc_stat = (int)IPC_STAT; int shmctl_ipc_info = (int)IPC_INFO; int shmctl_shm_info = (int)SHM_INFO; int shmctl_shm_stat = (int)SHM_STAT; #endif int map_fixed = MAP_FIXED; int af_inet = (int)AF_INET; int af_inet6 = (int)AF_INET6; uptr __sanitizer_in_addr_sz(int af) { if (af == AF_INET) return sizeof(struct in_addr); else if (af == AF_INET6) return sizeof(struct in6_addr); else return 0; } #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID int glob_nomatch = GLOB_NOMATCH; int glob_altdirfunc = GLOB_ALTDIRFUNC; #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID && \ (defined(__i386) || defined(__x86_64)) unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct); unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct); #ifdef __x86_64 unsigned struct_user_fpxregs_struct_sz = 0; #else unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct); #endif int ptrace_peektext = PTRACE_PEEKTEXT; int ptrace_peekdata = PTRACE_PEEKDATA; int ptrace_peekuser = PTRACE_PEEKUSER; int ptrace_getregs = PTRACE_GETREGS; int ptrace_setregs = PTRACE_SETREGS; int ptrace_getfpregs = PTRACE_GETFPREGS; int ptrace_setfpregs = PTRACE_SETFPREGS; int ptrace_getfpxregs = PTRACE_GETFPXREGS; int ptrace_setfpxregs = PTRACE_SETFPXREGS; int ptrace_geteventmsg = PTRACE_GETEVENTMSG; #if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) || \ (defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO)) int ptrace_getsiginfo = PTRACE_GETSIGINFO; int ptrace_setsiginfo = PTRACE_SETSIGINFO; #else int ptrace_getsiginfo = -1; int ptrace_setsiginfo = -1; #endif // PTRACE_GETSIGINFO/PTRACE_SETSIGINFO #if defined(PTRACE_GETREGSET) && defined(PTRACE_SETREGSET) int ptrace_getregset = PTRACE_GETREGSET; int ptrace_setregset = PTRACE_SETREGSET; #else int ptrace_getregset = -1; int ptrace_setregset = -1; #endif // PTRACE_GETREGSET/PTRACE_SETREGSET #endif unsigned path_max = PATH_MAX; // ioctl arguments unsigned struct_arpreq_sz = sizeof(struct arpreq); unsigned struct_ifreq_sz = sizeof(struct ifreq); unsigned struct_termios_sz = sizeof(struct termios); unsigned struct_winsize_sz = sizeof(struct winsize); #if SANITIZER_LINUX unsigned struct_cdrom_msf_sz = sizeof(struct cdrom_msf); unsigned struct_cdrom_multisession_sz = sizeof(struct cdrom_multisession); unsigned struct_cdrom_read_audio_sz = sizeof(struct cdrom_read_audio); unsigned struct_cdrom_subchnl_sz = sizeof(struct cdrom_subchnl); unsigned struct_cdrom_ti_sz = sizeof(struct cdrom_ti); unsigned struct_cdrom_tocentry_sz = sizeof(struct cdrom_tocentry); unsigned struct_cdrom_tochdr_sz = sizeof(struct cdrom_tochdr); unsigned struct_cdrom_volctrl_sz = sizeof(struct cdrom_volctrl); unsigned struct_ff_effect_sz = sizeof(struct ff_effect); unsigned struct_floppy_drive_params_sz = sizeof(struct floppy_drive_params); unsigned struct_floppy_drive_struct_sz = sizeof(struct floppy_drive_struct); unsigned struct_floppy_fdc_state_sz = sizeof(struct floppy_fdc_state); unsigned struct_floppy_max_errors_sz = sizeof(struct floppy_max_errors); unsigned struct_floppy_raw_cmd_sz = sizeof(struct floppy_raw_cmd); unsigned struct_floppy_struct_sz = sizeof(struct floppy_struct); unsigned struct_floppy_write_errors_sz = sizeof(struct floppy_write_errors); unsigned struct_format_descr_sz = sizeof(struct format_descr); unsigned struct_hd_driveid_sz = sizeof(struct hd_driveid); unsigned struct_hd_geometry_sz = sizeof(struct hd_geometry); unsigned struct_input_absinfo_sz = sizeof(struct input_absinfo); unsigned struct_input_id_sz = sizeof(struct input_id); unsigned struct_mtpos_sz = sizeof(struct mtpos); unsigned struct_termio_sz = sizeof(struct termio); unsigned struct_vt_consize_sz = sizeof(struct vt_consize); unsigned struct_vt_sizes_sz = sizeof(struct vt_sizes); unsigned struct_vt_stat_sz = sizeof(struct vt_stat); #endif // SANITIZER_LINUX #if SANITIZER_LINUX || SANITIZER_FREEBSD #if SOUND_VERSION >= 0x040000 unsigned struct_copr_buffer_sz = 0; unsigned struct_copr_debug_buf_sz = 0; unsigned struct_copr_msg_sz = 0; #else unsigned struct_copr_buffer_sz = sizeof(struct copr_buffer); unsigned struct_copr_debug_buf_sz = sizeof(struct copr_debug_buf); unsigned struct_copr_msg_sz = sizeof(struct copr_msg); #endif unsigned struct_midi_info_sz = sizeof(struct midi_info); unsigned struct_mtget_sz = sizeof(struct mtget); unsigned struct_mtop_sz = sizeof(struct mtop); unsigned struct_rtentry_sz = sizeof(struct rtentry); unsigned struct_sbi_instrument_sz = sizeof(struct sbi_instrument); unsigned struct_seq_event_rec_sz = sizeof(struct seq_event_rec); unsigned struct_synth_info_sz = sizeof(struct synth_info); unsigned struct_vt_mode_sz = sizeof(struct vt_mode); #endif // SANITIZER_LINUX || SANITIZER_FREEBSD #if SANITIZER_LINUX && !SANITIZER_ANDROID unsigned struct_ax25_parms_struct_sz = sizeof(struct ax25_parms_struct); unsigned struct_cyclades_monitor_sz = sizeof(struct cyclades_monitor); #if EV_VERSION > (0x010000) unsigned struct_input_keymap_entry_sz = sizeof(struct input_keymap_entry); #else unsigned struct_input_keymap_entry_sz = 0; #endif unsigned struct_ipx_config_data_sz = sizeof(struct ipx_config_data); unsigned struct_kbdiacrs_sz = sizeof(struct kbdiacrs); unsigned struct_kbentry_sz = sizeof(struct kbentry); unsigned struct_kbkeycode_sz = sizeof(struct kbkeycode); unsigned struct_kbsentry_sz = sizeof(struct kbsentry); unsigned struct_mtconfiginfo_sz = sizeof(struct mtconfiginfo); unsigned struct_nr_parms_struct_sz = sizeof(struct nr_parms_struct); unsigned struct_scc_modem_sz = sizeof(struct scc_modem); unsigned struct_scc_stat_sz = sizeof(struct scc_stat); unsigned struct_serial_multiport_struct_sz = sizeof(struct serial_multiport_struct); unsigned struct_serial_struct_sz = sizeof(struct serial_struct); unsigned struct_sockaddr_ax25_sz = sizeof(struct sockaddr_ax25); unsigned struct_unimapdesc_sz = sizeof(struct unimapdesc); unsigned struct_unimapinit_sz = sizeof(struct unimapinit); #endif // SANITIZER_LINUX && !SANITIZER_ANDROID #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info); unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats); #endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID #if !SANITIZER_ANDROID && !SANITIZER_MAC unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req); unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req); #endif const unsigned IOCTL_NOT_PRESENT = 0; unsigned IOCTL_FIOASYNC = FIOASYNC; unsigned IOCTL_FIOCLEX = FIOCLEX; unsigned IOCTL_FIOGETOWN = FIOGETOWN; unsigned IOCTL_FIONBIO = FIONBIO; unsigned IOCTL_FIONCLEX = FIONCLEX; unsigned IOCTL_FIOSETOWN = FIOSETOWN; unsigned IOCTL_SIOCADDMULTI = SIOCADDMULTI; unsigned IOCTL_SIOCATMARK = SIOCATMARK; unsigned IOCTL_SIOCDELMULTI = SIOCDELMULTI; unsigned IOCTL_SIOCGIFADDR = SIOCGIFADDR; unsigned IOCTL_SIOCGIFBRDADDR = SIOCGIFBRDADDR; unsigned IOCTL_SIOCGIFCONF = SIOCGIFCONF; unsigned IOCTL_SIOCGIFDSTADDR = SIOCGIFDSTADDR; unsigned IOCTL_SIOCGIFFLAGS = SIOCGIFFLAGS; unsigned IOCTL_SIOCGIFMETRIC = SIOCGIFMETRIC; unsigned IOCTL_SIOCGIFMTU = SIOCGIFMTU; unsigned IOCTL_SIOCGIFNETMASK = SIOCGIFNETMASK; unsigned IOCTL_SIOCGPGRP = SIOCGPGRP; unsigned IOCTL_SIOCSIFADDR = SIOCSIFADDR; unsigned IOCTL_SIOCSIFBRDADDR = SIOCSIFBRDADDR; unsigned IOCTL_SIOCSIFDSTADDR = SIOCSIFDSTADDR; unsigned IOCTL_SIOCSIFFLAGS = SIOCSIFFLAGS; unsigned IOCTL_SIOCSIFMETRIC = SIOCSIFMETRIC; unsigned IOCTL_SIOCSIFMTU = SIOCSIFMTU; unsigned IOCTL_SIOCSIFNETMASK = SIOCSIFNETMASK; unsigned IOCTL_SIOCSPGRP = SIOCSPGRP; unsigned IOCTL_TIOCCONS = TIOCCONS; unsigned IOCTL_TIOCEXCL = TIOCEXCL; unsigned IOCTL_TIOCGETD = TIOCGETD; unsigned IOCTL_TIOCGPGRP = TIOCGPGRP; unsigned IOCTL_TIOCGWINSZ = TIOCGWINSZ; unsigned IOCTL_TIOCMBIC = TIOCMBIC; unsigned IOCTL_TIOCMBIS = TIOCMBIS; unsigned IOCTL_TIOCMGET = TIOCMGET; unsigned IOCTL_TIOCMSET = TIOCMSET; unsigned IOCTL_TIOCNOTTY = TIOCNOTTY; unsigned IOCTL_TIOCNXCL = TIOCNXCL; unsigned IOCTL_TIOCOUTQ = TIOCOUTQ; unsigned IOCTL_TIOCPKT = TIOCPKT; unsigned IOCTL_TIOCSCTTY = TIOCSCTTY; unsigned IOCTL_TIOCSETD = TIOCSETD; unsigned IOCTL_TIOCSPGRP = TIOCSPGRP; unsigned IOCTL_TIOCSTI = TIOCSTI; unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ; #if ((SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID) unsigned IOCTL_SIOCGETSGCNT = SIOCGETSGCNT; unsigned IOCTL_SIOCGETVIFCNT = SIOCGETVIFCNT; #endif #if SANITIZER_LINUX unsigned IOCTL_EVIOCGABS = EVIOCGABS(0); unsigned IOCTL_EVIOCGBIT = EVIOCGBIT(0, 0); unsigned IOCTL_EVIOCGEFFECTS = EVIOCGEFFECTS; unsigned IOCTL_EVIOCGID = EVIOCGID; unsigned IOCTL_EVIOCGKEY = EVIOCGKEY(0); unsigned IOCTL_EVIOCGKEYCODE = EVIOCGKEYCODE; unsigned IOCTL_EVIOCGLED = EVIOCGLED(0); unsigned IOCTL_EVIOCGNAME = EVIOCGNAME(0); unsigned IOCTL_EVIOCGPHYS = EVIOCGPHYS(0); unsigned IOCTL_EVIOCGRAB = EVIOCGRAB; unsigned IOCTL_EVIOCGREP = EVIOCGREP; unsigned IOCTL_EVIOCGSND = EVIOCGSND(0); unsigned IOCTL_EVIOCGSW = EVIOCGSW(0); unsigned IOCTL_EVIOCGUNIQ = EVIOCGUNIQ(0); unsigned IOCTL_EVIOCGVERSION = EVIOCGVERSION; unsigned IOCTL_EVIOCRMFF = EVIOCRMFF; unsigned IOCTL_EVIOCSABS = EVIOCSABS(0); unsigned IOCTL_EVIOCSFF = EVIOCSFF; unsigned IOCTL_EVIOCSKEYCODE = EVIOCSKEYCODE; unsigned IOCTL_EVIOCSREP = EVIOCSREP; unsigned IOCTL_BLKFLSBUF = BLKFLSBUF; unsigned IOCTL_BLKGETSIZE = BLKGETSIZE; unsigned IOCTL_BLKRAGET = BLKRAGET; unsigned IOCTL_BLKRASET = BLKRASET; unsigned IOCTL_BLKROGET = BLKROGET; unsigned IOCTL_BLKROSET = BLKROSET; unsigned IOCTL_BLKRRPART = BLKRRPART; unsigned IOCTL_CDROMAUDIOBUFSIZ = CDROMAUDIOBUFSIZ; unsigned IOCTL_CDROMEJECT = CDROMEJECT; unsigned IOCTL_CDROMEJECT_SW = CDROMEJECT_SW; unsigned IOCTL_CDROMMULTISESSION = CDROMMULTISESSION; unsigned IOCTL_CDROMPAUSE = CDROMPAUSE; unsigned IOCTL_CDROMPLAYMSF = CDROMPLAYMSF; unsigned IOCTL_CDROMPLAYTRKIND = CDROMPLAYTRKIND; unsigned IOCTL_CDROMREADAUDIO = CDROMREADAUDIO; unsigned IOCTL_CDROMREADCOOKED = CDROMREADCOOKED; unsigned IOCTL_CDROMREADMODE1 = CDROMREADMODE1; unsigned IOCTL_CDROMREADMODE2 = CDROMREADMODE2; unsigned IOCTL_CDROMREADRAW = CDROMREADRAW; unsigned IOCTL_CDROMREADTOCENTRY = CDROMREADTOCENTRY; unsigned IOCTL_CDROMREADTOCHDR = CDROMREADTOCHDR; unsigned IOCTL_CDROMRESET = CDROMRESET; unsigned IOCTL_CDROMRESUME = CDROMRESUME; unsigned IOCTL_CDROMSEEK = CDROMSEEK; unsigned IOCTL_CDROMSTART = CDROMSTART; unsigned IOCTL_CDROMSTOP = CDROMSTOP; unsigned IOCTL_CDROMSUBCHNL = CDROMSUBCHNL; unsigned IOCTL_CDROMVOLCTRL = CDROMVOLCTRL; unsigned IOCTL_CDROMVOLREAD = CDROMVOLREAD; unsigned IOCTL_CDROM_GET_UPC = CDROM_GET_UPC; unsigned IOCTL_FDCLRPRM = FDCLRPRM; unsigned IOCTL_FDDEFPRM = FDDEFPRM; unsigned IOCTL_FDFLUSH = FDFLUSH; unsigned IOCTL_FDFMTBEG = FDFMTBEG; unsigned IOCTL_FDFMTEND = FDFMTEND; unsigned IOCTL_FDFMTTRK = FDFMTTRK; unsigned IOCTL_FDGETDRVPRM = FDGETDRVPRM; unsigned IOCTL_FDGETDRVSTAT = FDGETDRVSTAT; unsigned IOCTL_FDGETDRVTYP = FDGETDRVTYP; unsigned IOCTL_FDGETFDCSTAT = FDGETFDCSTAT; unsigned IOCTL_FDGETMAXERRS = FDGETMAXERRS; unsigned IOCTL_FDGETPRM = FDGETPRM; unsigned IOCTL_FDMSGOFF = FDMSGOFF; unsigned IOCTL_FDMSGON = FDMSGON; unsigned IOCTL_FDPOLLDRVSTAT = FDPOLLDRVSTAT; unsigned IOCTL_FDRAWCMD = FDRAWCMD; unsigned IOCTL_FDRESET = FDRESET; unsigned IOCTL_FDSETDRVPRM = FDSETDRVPRM; unsigned IOCTL_FDSETEMSGTRESH = FDSETEMSGTRESH; unsigned IOCTL_FDSETMAXERRS = FDSETMAXERRS; unsigned IOCTL_FDSETPRM = FDSETPRM; unsigned IOCTL_FDTWADDLE = FDTWADDLE; unsigned IOCTL_FDWERRORCLR = FDWERRORCLR; unsigned IOCTL_FDWERRORGET = FDWERRORGET; unsigned IOCTL_HDIO_DRIVE_CMD = HDIO_DRIVE_CMD; unsigned IOCTL_HDIO_GETGEO = HDIO_GETGEO; unsigned IOCTL_HDIO_GET_32BIT = HDIO_GET_32BIT; unsigned IOCTL_HDIO_GET_DMA = HDIO_GET_DMA; unsigned IOCTL_HDIO_GET_IDENTITY = HDIO_GET_IDENTITY; unsigned IOCTL_HDIO_GET_KEEPSETTINGS = HDIO_GET_KEEPSETTINGS; unsigned IOCTL_HDIO_GET_MULTCOUNT = HDIO_GET_MULTCOUNT; unsigned IOCTL_HDIO_GET_NOWERR = HDIO_GET_NOWERR; unsigned IOCTL_HDIO_GET_UNMASKINTR = HDIO_GET_UNMASKINTR; unsigned IOCTL_HDIO_SET_32BIT = HDIO_SET_32BIT; unsigned IOCTL_HDIO_SET_DMA = HDIO_SET_DMA; unsigned IOCTL_HDIO_SET_KEEPSETTINGS = HDIO_SET_KEEPSETTINGS; unsigned IOCTL_HDIO_SET_MULTCOUNT = HDIO_SET_MULTCOUNT; unsigned IOCTL_HDIO_SET_NOWERR = HDIO_SET_NOWERR; unsigned IOCTL_HDIO_SET_UNMASKINTR = HDIO_SET_UNMASKINTR; unsigned IOCTL_MTIOCPOS = MTIOCPOS; unsigned IOCTL_PPPIOCGASYNCMAP = PPPIOCGASYNCMAP; unsigned IOCTL_PPPIOCGDEBUG = PPPIOCGDEBUG; unsigned IOCTL_PPPIOCGFLAGS = PPPIOCGFLAGS; unsigned IOCTL_PPPIOCGUNIT = PPPIOCGUNIT; unsigned IOCTL_PPPIOCGXASYNCMAP = PPPIOCGXASYNCMAP; unsigned IOCTL_PPPIOCSASYNCMAP = PPPIOCSASYNCMAP; unsigned IOCTL_PPPIOCSDEBUG = PPPIOCSDEBUG; unsigned IOCTL_PPPIOCSFLAGS = PPPIOCSFLAGS; unsigned IOCTL_PPPIOCSMAXCID = PPPIOCSMAXCID; unsigned IOCTL_PPPIOCSMRU = PPPIOCSMRU; unsigned IOCTL_PPPIOCSXASYNCMAP = PPPIOCSXASYNCMAP; unsigned IOCTL_SIOCADDRT = SIOCADDRT; unsigned IOCTL_SIOCDARP = SIOCDARP; unsigned IOCTL_SIOCDELRT = SIOCDELRT; unsigned IOCTL_SIOCDRARP = SIOCDRARP; unsigned IOCTL_SIOCGARP = SIOCGARP; unsigned IOCTL_SIOCGIFENCAP = SIOCGIFENCAP; unsigned IOCTL_SIOCGIFHWADDR = SIOCGIFHWADDR; unsigned IOCTL_SIOCGIFMAP = SIOCGIFMAP; unsigned IOCTL_SIOCGIFMEM = SIOCGIFMEM; unsigned IOCTL_SIOCGIFNAME = SIOCGIFNAME; unsigned IOCTL_SIOCGIFSLAVE = SIOCGIFSLAVE; unsigned IOCTL_SIOCGRARP = SIOCGRARP; unsigned IOCTL_SIOCGSTAMP = SIOCGSTAMP; unsigned IOCTL_SIOCSARP = SIOCSARP; unsigned IOCTL_SIOCSIFENCAP = SIOCSIFENCAP; unsigned IOCTL_SIOCSIFHWADDR = SIOCSIFHWADDR; unsigned IOCTL_SIOCSIFLINK = SIOCSIFLINK; unsigned IOCTL_SIOCSIFMAP = SIOCSIFMAP; unsigned IOCTL_SIOCSIFMEM = SIOCSIFMEM; unsigned IOCTL_SIOCSIFSLAVE = SIOCSIFSLAVE; unsigned IOCTL_SIOCSRARP = SIOCSRARP; # if SOUND_VERSION >= 0x040000 unsigned IOCTL_SNDCTL_COPR_HALT = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_LOAD = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_RCODE = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_RCVMSG = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_RDATA = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_RESET = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_RUN = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_SENDMSG = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_WCODE = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_WDATA = IOCTL_NOT_PRESENT; unsigned IOCTL_SOUND_PCM_READ_BITS = IOCTL_NOT_PRESENT; unsigned IOCTL_SOUND_PCM_READ_CHANNELS = IOCTL_NOT_PRESENT; unsigned IOCTL_SOUND_PCM_READ_FILTER = IOCTL_NOT_PRESENT; unsigned IOCTL_SOUND_PCM_READ_RATE = IOCTL_NOT_PRESENT; unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = IOCTL_NOT_PRESENT; unsigned IOCTL_SOUND_PCM_WRITE_FILTER = IOCTL_NOT_PRESENT; # else // SOUND_VERSION unsigned IOCTL_SNDCTL_COPR_HALT = SNDCTL_COPR_HALT; unsigned IOCTL_SNDCTL_COPR_LOAD = SNDCTL_COPR_LOAD; unsigned IOCTL_SNDCTL_COPR_RCODE = SNDCTL_COPR_RCODE; unsigned IOCTL_SNDCTL_COPR_RCVMSG = SNDCTL_COPR_RCVMSG; unsigned IOCTL_SNDCTL_COPR_RDATA = SNDCTL_COPR_RDATA; unsigned IOCTL_SNDCTL_COPR_RESET = SNDCTL_COPR_RESET; unsigned IOCTL_SNDCTL_COPR_RUN = SNDCTL_COPR_RUN; unsigned IOCTL_SNDCTL_COPR_SENDMSG = SNDCTL_COPR_SENDMSG; unsigned IOCTL_SNDCTL_COPR_WCODE = SNDCTL_COPR_WCODE; unsigned IOCTL_SNDCTL_COPR_WDATA = SNDCTL_COPR_WDATA; unsigned IOCTL_SOUND_PCM_READ_BITS = SOUND_PCM_READ_BITS; unsigned IOCTL_SOUND_PCM_READ_CHANNELS = SOUND_PCM_READ_CHANNELS; unsigned IOCTL_SOUND_PCM_READ_FILTER = SOUND_PCM_READ_FILTER; unsigned IOCTL_SOUND_PCM_READ_RATE = SOUND_PCM_READ_RATE; unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = SOUND_PCM_WRITE_CHANNELS; unsigned IOCTL_SOUND_PCM_WRITE_FILTER = SOUND_PCM_WRITE_FILTER; #endif // SOUND_VERSION unsigned IOCTL_TCFLSH = TCFLSH; unsigned IOCTL_TCGETA = TCGETA; unsigned IOCTL_TCGETS = TCGETS; unsigned IOCTL_TCSBRK = TCSBRK; unsigned IOCTL_TCSBRKP = TCSBRKP; unsigned IOCTL_TCSETA = TCSETA; unsigned IOCTL_TCSETAF = TCSETAF; unsigned IOCTL_TCSETAW = TCSETAW; unsigned IOCTL_TCSETS = TCSETS; unsigned IOCTL_TCSETSF = TCSETSF; unsigned IOCTL_TCSETSW = TCSETSW; unsigned IOCTL_TCXONC = TCXONC; unsigned IOCTL_TIOCGLCKTRMIOS = TIOCGLCKTRMIOS; unsigned IOCTL_TIOCGSOFTCAR = TIOCGSOFTCAR; unsigned IOCTL_TIOCINQ = TIOCINQ; unsigned IOCTL_TIOCLINUX = TIOCLINUX; unsigned IOCTL_TIOCSERCONFIG = TIOCSERCONFIG; unsigned IOCTL_TIOCSERGETLSR = TIOCSERGETLSR; unsigned IOCTL_TIOCSERGWILD = TIOCSERGWILD; unsigned IOCTL_TIOCSERSWILD = TIOCSERSWILD; unsigned IOCTL_TIOCSLCKTRMIOS = TIOCSLCKTRMIOS; unsigned IOCTL_TIOCSSOFTCAR = TIOCSSOFTCAR; unsigned IOCTL_VT_DISALLOCATE = VT_DISALLOCATE; unsigned IOCTL_VT_GETSTATE = VT_GETSTATE; unsigned IOCTL_VT_RESIZE = VT_RESIZE; unsigned IOCTL_VT_RESIZEX = VT_RESIZEX; unsigned IOCTL_VT_SENDSIG = VT_SENDSIG; #endif // SANITIZER_LINUX #if SANITIZER_LINUX || SANITIZER_FREEBSD unsigned IOCTL_MTIOCGET = MTIOCGET; unsigned IOCTL_MTIOCTOP = MTIOCTOP; unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE = SNDCTL_DSP_GETBLKSIZE; unsigned IOCTL_SNDCTL_DSP_GETFMTS = SNDCTL_DSP_GETFMTS; unsigned IOCTL_SNDCTL_DSP_NONBLOCK = SNDCTL_DSP_NONBLOCK; unsigned IOCTL_SNDCTL_DSP_POST = SNDCTL_DSP_POST; unsigned IOCTL_SNDCTL_DSP_RESET = SNDCTL_DSP_RESET; unsigned IOCTL_SNDCTL_DSP_SETFMT = SNDCTL_DSP_SETFMT; unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT = SNDCTL_DSP_SETFRAGMENT; unsigned IOCTL_SNDCTL_DSP_SPEED = SNDCTL_DSP_SPEED; unsigned IOCTL_SNDCTL_DSP_STEREO = SNDCTL_DSP_STEREO; unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE = SNDCTL_DSP_SUBDIVIDE; unsigned IOCTL_SNDCTL_DSP_SYNC = SNDCTL_DSP_SYNC; unsigned IOCTL_SNDCTL_FM_4OP_ENABLE = SNDCTL_FM_4OP_ENABLE; unsigned IOCTL_SNDCTL_FM_LOAD_INSTR = SNDCTL_FM_LOAD_INSTR; unsigned IOCTL_SNDCTL_MIDI_INFO = SNDCTL_MIDI_INFO; unsigned IOCTL_SNDCTL_MIDI_PRETIME = SNDCTL_MIDI_PRETIME; unsigned IOCTL_SNDCTL_SEQ_CTRLRATE = SNDCTL_SEQ_CTRLRATE; unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT = SNDCTL_SEQ_GETINCOUNT; unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT = SNDCTL_SEQ_GETOUTCOUNT; unsigned IOCTL_SNDCTL_SEQ_NRMIDIS = SNDCTL_SEQ_NRMIDIS; unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS = SNDCTL_SEQ_NRSYNTHS; unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND = SNDCTL_SEQ_OUTOFBAND; unsigned IOCTL_SNDCTL_SEQ_PANIC = SNDCTL_SEQ_PANIC; unsigned IOCTL_SNDCTL_SEQ_PERCMODE = SNDCTL_SEQ_PERCMODE; unsigned IOCTL_SNDCTL_SEQ_RESET = SNDCTL_SEQ_RESET; unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES = SNDCTL_SEQ_RESETSAMPLES; unsigned IOCTL_SNDCTL_SEQ_SYNC = SNDCTL_SEQ_SYNC; unsigned IOCTL_SNDCTL_SEQ_TESTMIDI = SNDCTL_SEQ_TESTMIDI; unsigned IOCTL_SNDCTL_SEQ_THRESHOLD = SNDCTL_SEQ_THRESHOLD; unsigned IOCTL_SNDCTL_SYNTH_INFO = SNDCTL_SYNTH_INFO; unsigned IOCTL_SNDCTL_SYNTH_MEMAVL = SNDCTL_SYNTH_MEMAVL; unsigned IOCTL_SNDCTL_TMR_CONTINUE = SNDCTL_TMR_CONTINUE; unsigned IOCTL_SNDCTL_TMR_METRONOME = SNDCTL_TMR_METRONOME; unsigned IOCTL_SNDCTL_TMR_SELECT = SNDCTL_TMR_SELECT; unsigned IOCTL_SNDCTL_TMR_SOURCE = SNDCTL_TMR_SOURCE; unsigned IOCTL_SNDCTL_TMR_START = SNDCTL_TMR_START; unsigned IOCTL_SNDCTL_TMR_STOP = SNDCTL_TMR_STOP; unsigned IOCTL_SNDCTL_TMR_TEMPO = SNDCTL_TMR_TEMPO; unsigned IOCTL_SNDCTL_TMR_TIMEBASE = SNDCTL_TMR_TIMEBASE; unsigned IOCTL_SOUND_MIXER_READ_ALTPCM = SOUND_MIXER_READ_ALTPCM; unsigned IOCTL_SOUND_MIXER_READ_BASS = SOUND_MIXER_READ_BASS; unsigned IOCTL_SOUND_MIXER_READ_CAPS = SOUND_MIXER_READ_CAPS; unsigned IOCTL_SOUND_MIXER_READ_CD = SOUND_MIXER_READ_CD; unsigned IOCTL_SOUND_MIXER_READ_DEVMASK = SOUND_MIXER_READ_DEVMASK; unsigned IOCTL_SOUND_MIXER_READ_ENHANCE = SOUND_MIXER_READ_ENHANCE; unsigned IOCTL_SOUND_MIXER_READ_IGAIN = SOUND_MIXER_READ_IGAIN; unsigned IOCTL_SOUND_MIXER_READ_IMIX = SOUND_MIXER_READ_IMIX; unsigned IOCTL_SOUND_MIXER_READ_LINE = SOUND_MIXER_READ_LINE; unsigned IOCTL_SOUND_MIXER_READ_LINE1 = SOUND_MIXER_READ_LINE1; unsigned IOCTL_SOUND_MIXER_READ_LINE2 = SOUND_MIXER_READ_LINE2; unsigned IOCTL_SOUND_MIXER_READ_LINE3 = SOUND_MIXER_READ_LINE3; unsigned IOCTL_SOUND_MIXER_READ_LOUD = SOUND_MIXER_READ_LOUD; unsigned IOCTL_SOUND_MIXER_READ_MIC = SOUND_MIXER_READ_MIC; unsigned IOCTL_SOUND_MIXER_READ_MUTE = SOUND_MIXER_READ_MUTE; unsigned IOCTL_SOUND_MIXER_READ_OGAIN = SOUND_MIXER_READ_OGAIN; unsigned IOCTL_SOUND_MIXER_READ_PCM = SOUND_MIXER_READ_PCM; unsigned IOCTL_SOUND_MIXER_READ_RECLEV = SOUND_MIXER_READ_RECLEV; unsigned IOCTL_SOUND_MIXER_READ_RECMASK = SOUND_MIXER_READ_RECMASK; unsigned IOCTL_SOUND_MIXER_READ_RECSRC = SOUND_MIXER_READ_RECSRC; unsigned IOCTL_SOUND_MIXER_READ_SPEAKER = SOUND_MIXER_READ_SPEAKER; unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS = SOUND_MIXER_READ_STEREODEVS; unsigned IOCTL_SOUND_MIXER_READ_SYNTH = SOUND_MIXER_READ_SYNTH; unsigned IOCTL_SOUND_MIXER_READ_TREBLE = SOUND_MIXER_READ_TREBLE; unsigned IOCTL_SOUND_MIXER_READ_VOLUME = SOUND_MIXER_READ_VOLUME; unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM = SOUND_MIXER_WRITE_ALTPCM; unsigned IOCTL_SOUND_MIXER_WRITE_BASS = SOUND_MIXER_WRITE_BASS; unsigned IOCTL_SOUND_MIXER_WRITE_CD = SOUND_MIXER_WRITE_CD; unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE = SOUND_MIXER_WRITE_ENHANCE; unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN = SOUND_MIXER_WRITE_IGAIN; unsigned IOCTL_SOUND_MIXER_WRITE_IMIX = SOUND_MIXER_WRITE_IMIX; unsigned IOCTL_SOUND_MIXER_WRITE_LINE = SOUND_MIXER_WRITE_LINE; unsigned IOCTL_SOUND_MIXER_WRITE_LINE1 = SOUND_MIXER_WRITE_LINE1; unsigned IOCTL_SOUND_MIXER_WRITE_LINE2 = SOUND_MIXER_WRITE_LINE2; unsigned IOCTL_SOUND_MIXER_WRITE_LINE3 = SOUND_MIXER_WRITE_LINE3; unsigned IOCTL_SOUND_MIXER_WRITE_LOUD = SOUND_MIXER_WRITE_LOUD; unsigned IOCTL_SOUND_MIXER_WRITE_MIC = SOUND_MIXER_WRITE_MIC; unsigned IOCTL_SOUND_MIXER_WRITE_MUTE = SOUND_MIXER_WRITE_MUTE; unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN = SOUND_MIXER_WRITE_OGAIN; unsigned IOCTL_SOUND_MIXER_WRITE_PCM = SOUND_MIXER_WRITE_PCM; unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV = SOUND_MIXER_WRITE_RECLEV; unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC = SOUND_MIXER_WRITE_RECSRC; unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER = SOUND_MIXER_WRITE_SPEAKER; unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH = SOUND_MIXER_WRITE_SYNTH; unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE = SOUND_MIXER_WRITE_TREBLE; unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME = SOUND_MIXER_WRITE_VOLUME; unsigned IOCTL_VT_ACTIVATE = VT_ACTIVATE; unsigned IOCTL_VT_GETMODE = VT_GETMODE; unsigned IOCTL_VT_OPENQRY = VT_OPENQRY; unsigned IOCTL_VT_RELDISP = VT_RELDISP; unsigned IOCTL_VT_SETMODE = VT_SETMODE; unsigned IOCTL_VT_WAITACTIVE = VT_WAITACTIVE; #endif // SANITIZER_LINUX || SANITIZER_FREEBSD #if SANITIZER_LINUX && !SANITIZER_ANDROID unsigned IOCTL_CYGETDEFTHRESH = CYGETDEFTHRESH; unsigned IOCTL_CYGETDEFTIMEOUT = CYGETDEFTIMEOUT; unsigned IOCTL_CYGETMON = CYGETMON; unsigned IOCTL_CYGETTHRESH = CYGETTHRESH; unsigned IOCTL_CYGETTIMEOUT = CYGETTIMEOUT; unsigned IOCTL_CYSETDEFTHRESH = CYSETDEFTHRESH; unsigned IOCTL_CYSETDEFTIMEOUT = CYSETDEFTIMEOUT; unsigned IOCTL_CYSETTHRESH = CYSETTHRESH; unsigned IOCTL_CYSETTIMEOUT = CYSETTIMEOUT; unsigned IOCTL_EQL_EMANCIPATE = EQL_EMANCIPATE; unsigned IOCTL_EQL_ENSLAVE = EQL_ENSLAVE; unsigned IOCTL_EQL_GETMASTRCFG = EQL_GETMASTRCFG; unsigned IOCTL_EQL_GETSLAVECFG = EQL_GETSLAVECFG; unsigned IOCTL_EQL_SETMASTRCFG = EQL_SETMASTRCFG; unsigned IOCTL_EQL_SETSLAVECFG = EQL_SETSLAVECFG; #if EV_VERSION > (0x010000) unsigned IOCTL_EVIOCGKEYCODE_V2 = EVIOCGKEYCODE_V2; unsigned IOCTL_EVIOCGPROP = EVIOCGPROP(0); unsigned IOCTL_EVIOCSKEYCODE_V2 = EVIOCSKEYCODE_V2; #else unsigned IOCTL_EVIOCGKEYCODE_V2 = IOCTL_NOT_PRESENT; unsigned IOCTL_EVIOCGPROP = IOCTL_NOT_PRESENT; unsigned IOCTL_EVIOCSKEYCODE_V2 = IOCTL_NOT_PRESENT; #endif unsigned IOCTL_FS_IOC_GETFLAGS = FS_IOC_GETFLAGS; unsigned IOCTL_FS_IOC_GETVERSION = FS_IOC_GETVERSION; unsigned IOCTL_FS_IOC_SETFLAGS = FS_IOC_SETFLAGS; unsigned IOCTL_FS_IOC_SETVERSION = FS_IOC_SETVERSION; unsigned IOCTL_GIO_CMAP = GIO_CMAP; unsigned IOCTL_GIO_FONT = GIO_FONT; unsigned IOCTL_GIO_UNIMAP = GIO_UNIMAP; unsigned IOCTL_GIO_UNISCRNMAP = GIO_UNISCRNMAP; unsigned IOCTL_KDADDIO = KDADDIO; unsigned IOCTL_KDDELIO = KDDELIO; unsigned IOCTL_KDGETKEYCODE = KDGETKEYCODE; unsigned IOCTL_KDGKBDIACR = KDGKBDIACR; unsigned IOCTL_KDGKBENT = KDGKBENT; unsigned IOCTL_KDGKBLED = KDGKBLED; unsigned IOCTL_KDGKBMETA = KDGKBMETA; unsigned IOCTL_KDGKBSENT = KDGKBSENT; unsigned IOCTL_KDMAPDISP = KDMAPDISP; unsigned IOCTL_KDSETKEYCODE = KDSETKEYCODE; unsigned IOCTL_KDSIGACCEPT = KDSIGACCEPT; unsigned IOCTL_KDSKBDIACR = KDSKBDIACR; unsigned IOCTL_KDSKBENT = KDSKBENT; unsigned IOCTL_KDSKBLED = KDSKBLED; unsigned IOCTL_KDSKBMETA = KDSKBMETA; unsigned IOCTL_KDSKBSENT = KDSKBSENT; unsigned IOCTL_KDUNMAPDISP = KDUNMAPDISP; unsigned IOCTL_LPABORT = LPABORT; unsigned IOCTL_LPABORTOPEN = LPABORTOPEN; unsigned IOCTL_LPCAREFUL = LPCAREFUL; unsigned IOCTL_LPCHAR = LPCHAR; unsigned IOCTL_LPGETIRQ = LPGETIRQ; unsigned IOCTL_LPGETSTATUS = LPGETSTATUS; unsigned IOCTL_LPRESET = LPRESET; unsigned IOCTL_LPSETIRQ = LPSETIRQ; unsigned IOCTL_LPTIME = LPTIME; unsigned IOCTL_LPWAIT = LPWAIT; unsigned IOCTL_MTIOCGETCONFIG = MTIOCGETCONFIG; unsigned IOCTL_MTIOCSETCONFIG = MTIOCSETCONFIG; unsigned IOCTL_PIO_CMAP = PIO_CMAP; unsigned IOCTL_PIO_FONT = PIO_FONT; unsigned IOCTL_PIO_UNIMAP = PIO_UNIMAP; unsigned IOCTL_PIO_UNIMAPCLR = PIO_UNIMAPCLR; unsigned IOCTL_PIO_UNISCRNMAP = PIO_UNISCRNMAP; unsigned IOCTL_SCSI_IOCTL_GET_IDLUN = SCSI_IOCTL_GET_IDLUN; unsigned IOCTL_SCSI_IOCTL_PROBE_HOST = SCSI_IOCTL_PROBE_HOST; unsigned IOCTL_SCSI_IOCTL_TAGGED_DISABLE = SCSI_IOCTL_TAGGED_DISABLE; unsigned IOCTL_SCSI_IOCTL_TAGGED_ENABLE = SCSI_IOCTL_TAGGED_ENABLE; unsigned IOCTL_SIOCAIPXITFCRT = SIOCAIPXITFCRT; unsigned IOCTL_SIOCAIPXPRISLT = SIOCAIPXPRISLT; unsigned IOCTL_SIOCAX25ADDUID = SIOCAX25ADDUID; unsigned IOCTL_SIOCAX25DELUID = SIOCAX25DELUID; unsigned IOCTL_SIOCAX25GETPARMS = SIOCAX25GETPARMS; unsigned IOCTL_SIOCAX25GETUID = SIOCAX25GETUID; unsigned IOCTL_SIOCAX25NOUID = SIOCAX25NOUID; unsigned IOCTL_SIOCAX25SETPARMS = SIOCAX25SETPARMS; unsigned IOCTL_SIOCDEVPLIP = SIOCDEVPLIP; unsigned IOCTL_SIOCIPXCFGDATA = SIOCIPXCFGDATA; unsigned IOCTL_SIOCNRDECOBS = SIOCNRDECOBS; unsigned IOCTL_SIOCNRGETPARMS = SIOCNRGETPARMS; unsigned IOCTL_SIOCNRRTCTL = SIOCNRRTCTL; unsigned IOCTL_SIOCNRSETPARMS = SIOCNRSETPARMS; unsigned IOCTL_TIOCGSERIAL = TIOCGSERIAL; unsigned IOCTL_TIOCSERGETMULTI = TIOCSERGETMULTI; unsigned IOCTL_TIOCSERSETMULTI = TIOCSERSETMULTI; unsigned IOCTL_TIOCSSERIAL = TIOCSSERIAL; #endif // SANITIZER_LINUX && !SANITIZER_ANDROID #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID unsigned IOCTL_GIO_SCRNMAP = GIO_SCRNMAP; unsigned IOCTL_KDDISABIO = KDDISABIO; unsigned IOCTL_KDENABIO = KDENABIO; unsigned IOCTL_KDGETLED = KDGETLED; unsigned IOCTL_KDGETMODE = KDGETMODE; unsigned IOCTL_KDGKBMODE = KDGKBMODE; unsigned IOCTL_KDGKBTYPE = KDGKBTYPE; unsigned IOCTL_KDMKTONE = KDMKTONE; unsigned IOCTL_KDSETLED = KDSETLED; unsigned IOCTL_KDSETMODE = KDSETMODE; unsigned IOCTL_KDSKBMODE = KDSKBMODE; unsigned IOCTL_KIOCSOUND = KIOCSOUND; unsigned IOCTL_PIO_SCRNMAP = PIO_SCRNMAP; unsigned IOCTL_SNDCTL_DSP_GETISPACE = SNDCTL_DSP_GETISPACE; unsigned IOCTL_SNDCTL_DSP_GETOSPACE = SNDCTL_DSP_GETOSPACE; #endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID const int errno_EINVAL = EINVAL; // EOWNERDEAD is not present in some older platforms. #if defined(EOWNERDEAD) const int errno_EOWNERDEAD = EOWNERDEAD; #else const int errno_EOWNERDEAD = -1; #endif const int si_SEGV_MAPERR = SEGV_MAPERR; const int si_SEGV_ACCERR = SEGV_ACCERR; } // namespace __sanitizer COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t)); COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned)); CHECK_TYPE_SIZE(pthread_key_t); #if SANITIZER_LINUX // FIXME: We define those on Linux and Mac, but only check on Linux. COMPILER_CHECK(IOC_NRBITS == _IOC_NRBITS); COMPILER_CHECK(IOC_TYPEBITS == _IOC_TYPEBITS); COMPILER_CHECK(IOC_SIZEBITS == _IOC_SIZEBITS); COMPILER_CHECK(IOC_DIRBITS == _IOC_DIRBITS); COMPILER_CHECK(IOC_NRMASK == _IOC_NRMASK); COMPILER_CHECK(IOC_TYPEMASK == _IOC_TYPEMASK); COMPILER_CHECK(IOC_SIZEMASK == _IOC_SIZEMASK); COMPILER_CHECK(IOC_DIRMASK == _IOC_DIRMASK); COMPILER_CHECK(IOC_NRSHIFT == _IOC_NRSHIFT); COMPILER_CHECK(IOC_TYPESHIFT == _IOC_TYPESHIFT); COMPILER_CHECK(IOC_SIZESHIFT == _IOC_SIZESHIFT); COMPILER_CHECK(IOC_DIRSHIFT == _IOC_DIRSHIFT); COMPILER_CHECK(IOC_NONE == _IOC_NONE); COMPILER_CHECK(IOC_WRITE == _IOC_WRITE); COMPILER_CHECK(IOC_READ == _IOC_READ); COMPILER_CHECK(EVIOC_ABS_MAX == ABS_MAX); COMPILER_CHECK(EVIOC_EV_MAX == EV_MAX); COMPILER_CHECK(IOC_SIZE(0x12345678) == _IOC_SIZE(0x12345678)); COMPILER_CHECK(IOC_DIR(0x12345678) == _IOC_DIR(0x12345678)); COMPILER_CHECK(IOC_NR(0x12345678) == _IOC_NR(0x12345678)); COMPILER_CHECK(IOC_TYPE(0x12345678) == _IOC_TYPE(0x12345678)); #endif // SANITIZER_LINUX #if SANITIZER_LINUX || SANITIZER_FREEBSD // There are more undocumented fields in dl_phdr_info that we are not interested // in. COMPILER_CHECK(sizeof(__sanitizer_dl_phdr_info) <= sizeof(dl_phdr_info)); CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr); CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name); CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr); CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum); #endif // SANITIZER_LINUX || SANITIZER_FREEBSD #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID CHECK_TYPE_SIZE(glob_t); CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc); CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv); CHECK_SIZE_AND_OFFSET(glob_t, gl_offs); CHECK_SIZE_AND_OFFSET(glob_t, gl_flags); CHECK_SIZE_AND_OFFSET(glob_t, gl_closedir); CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir); CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir); CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat); CHECK_SIZE_AND_OFFSET(glob_t, gl_stat); #endif CHECK_TYPE_SIZE(addrinfo); CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags); CHECK_SIZE_AND_OFFSET(addrinfo, ai_family); CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype); CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol); CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol); CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen); CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname); CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr); CHECK_TYPE_SIZE(hostent); CHECK_SIZE_AND_OFFSET(hostent, h_name); CHECK_SIZE_AND_OFFSET(hostent, h_aliases); CHECK_SIZE_AND_OFFSET(hostent, h_addrtype); CHECK_SIZE_AND_OFFSET(hostent, h_length); CHECK_SIZE_AND_OFFSET(hostent, h_addr_list); CHECK_TYPE_SIZE(iovec); CHECK_SIZE_AND_OFFSET(iovec, iov_base); CHECK_SIZE_AND_OFFSET(iovec, iov_len); CHECK_TYPE_SIZE(msghdr); CHECK_SIZE_AND_OFFSET(msghdr, msg_name); CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen); CHECK_SIZE_AND_OFFSET(msghdr, msg_iov); CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen); CHECK_SIZE_AND_OFFSET(msghdr, msg_control); CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen); CHECK_SIZE_AND_OFFSET(msghdr, msg_flags); CHECK_TYPE_SIZE(cmsghdr); CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len); CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level); CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type); COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent)); CHECK_SIZE_AND_OFFSET(dirent, d_ino); #if SANITIZER_MAC CHECK_SIZE_AND_OFFSET(dirent, d_seekoff); #elif SANITIZER_FREEBSD // There is no 'd_off' field on FreeBSD. #else CHECK_SIZE_AND_OFFSET(dirent, d_off); #endif CHECK_SIZE_AND_OFFSET(dirent, d_reclen); #if SANITIZER_LINUX && !SANITIZER_ANDROID COMPILER_CHECK(sizeof(__sanitizer_dirent64) <= sizeof(dirent64)); CHECK_SIZE_AND_OFFSET(dirent64, d_ino); CHECK_SIZE_AND_OFFSET(dirent64, d_off); CHECK_SIZE_AND_OFFSET(dirent64, d_reclen); #endif CHECK_TYPE_SIZE(ifconf); CHECK_SIZE_AND_OFFSET(ifconf, ifc_len); CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu); CHECK_TYPE_SIZE(pollfd); CHECK_SIZE_AND_OFFSET(pollfd, fd); CHECK_SIZE_AND_OFFSET(pollfd, events); CHECK_SIZE_AND_OFFSET(pollfd, revents); CHECK_TYPE_SIZE(nfds_t); CHECK_TYPE_SIZE(sigset_t); COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction)); // Can't write checks for sa_handler and sa_sigaction due to them being // preprocessor macros. CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask); CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_flags); #if SANITIZER_LINUX CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_restorer); #endif #if SANITIZER_LINUX CHECK_TYPE_SIZE(__sysctl_args); CHECK_SIZE_AND_OFFSET(__sysctl_args, name); CHECK_SIZE_AND_OFFSET(__sysctl_args, nlen); CHECK_SIZE_AND_OFFSET(__sysctl_args, oldval); CHECK_SIZE_AND_OFFSET(__sysctl_args, oldlenp); CHECK_SIZE_AND_OFFSET(__sysctl_args, newval); CHECK_SIZE_AND_OFFSET(__sysctl_args, newlen); CHECK_TYPE_SIZE(__kernel_uid_t); CHECK_TYPE_SIZE(__kernel_gid_t); #if !defined(__aarch64__) CHECK_TYPE_SIZE(__kernel_old_uid_t); CHECK_TYPE_SIZE(__kernel_old_gid_t); #endif CHECK_TYPE_SIZE(__kernel_off_t); CHECK_TYPE_SIZE(__kernel_loff_t); CHECK_TYPE_SIZE(__kernel_fd_set); #endif #if !SANITIZER_ANDROID CHECK_TYPE_SIZE(wordexp_t); CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc); CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv); CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs); #endif CHECK_TYPE_SIZE(tm); CHECK_SIZE_AND_OFFSET(tm, tm_sec); CHECK_SIZE_AND_OFFSET(tm, tm_min); CHECK_SIZE_AND_OFFSET(tm, tm_hour); CHECK_SIZE_AND_OFFSET(tm, tm_mday); CHECK_SIZE_AND_OFFSET(tm, tm_mon); CHECK_SIZE_AND_OFFSET(tm, tm_year); CHECK_SIZE_AND_OFFSET(tm, tm_wday); CHECK_SIZE_AND_OFFSET(tm, tm_yday); CHECK_SIZE_AND_OFFSET(tm, tm_isdst); CHECK_SIZE_AND_OFFSET(tm, tm_gmtoff); CHECK_SIZE_AND_OFFSET(tm, tm_zone); #if SANITIZER_LINUX CHECK_TYPE_SIZE(mntent); CHECK_SIZE_AND_OFFSET(mntent, mnt_fsname); CHECK_SIZE_AND_OFFSET(mntent, mnt_dir); CHECK_SIZE_AND_OFFSET(mntent, mnt_type); CHECK_SIZE_AND_OFFSET(mntent, mnt_opts); CHECK_SIZE_AND_OFFSET(mntent, mnt_freq); CHECK_SIZE_AND_OFFSET(mntent, mnt_passno); #endif CHECK_TYPE_SIZE(ether_addr); #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID CHECK_TYPE_SIZE(ipc_perm); # if SANITIZER_FREEBSD CHECK_SIZE_AND_OFFSET(ipc_perm, key); CHECK_SIZE_AND_OFFSET(ipc_perm, seq); # else CHECK_SIZE_AND_OFFSET(ipc_perm, __key); CHECK_SIZE_AND_OFFSET(ipc_perm, __seq); # endif CHECK_SIZE_AND_OFFSET(ipc_perm, uid); CHECK_SIZE_AND_OFFSET(ipc_perm, gid); CHECK_SIZE_AND_OFFSET(ipc_perm, cuid); CHECK_SIZE_AND_OFFSET(ipc_perm, cgid); CHECK_SIZE_AND_OFFSET(ipc_perm, mode); CHECK_TYPE_SIZE(shmid_ds); CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm); CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz); CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime); CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime); CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime); CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid); CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid); CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch); #endif CHECK_TYPE_SIZE(clock_t); #if SANITIZER_LINUX CHECK_TYPE_SIZE(clockid_t); #endif #if !SANITIZER_ANDROID CHECK_TYPE_SIZE(ifaddrs); CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next); CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name); CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_addr); CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask); #if SANITIZER_LINUX || SANITIZER_FREEBSD // Compare against the union, because we can't reach into the union in a // compliant way. #ifdef ifa_dstaddr #undef ifa_dstaddr #endif # if SANITIZER_FREEBSD CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr); # else COMPILER_CHECK(sizeof(((__sanitizer_ifaddrs *)NULL)->ifa_dstaddr) == sizeof(((ifaddrs *)NULL)->ifa_ifu)); COMPILER_CHECK(offsetof(__sanitizer_ifaddrs, ifa_dstaddr) == offsetof(ifaddrs, ifa_ifu)); # endif // SANITIZER_FREEBSD #else CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr); #endif // SANITIZER_LINUX CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data); #endif #if SANITIZER_LINUX COMPILER_CHECK(sizeof(__sanitizer_mallinfo) == sizeof(struct mallinfo)); #endif #if !SANITIZER_ANDROID CHECK_TYPE_SIZE(timeb); CHECK_SIZE_AND_OFFSET(timeb, time); CHECK_SIZE_AND_OFFSET(timeb, millitm); CHECK_SIZE_AND_OFFSET(timeb, timezone); CHECK_SIZE_AND_OFFSET(timeb, dstflag); #endif CHECK_TYPE_SIZE(passwd); CHECK_SIZE_AND_OFFSET(passwd, pw_name); CHECK_SIZE_AND_OFFSET(passwd, pw_passwd); CHECK_SIZE_AND_OFFSET(passwd, pw_uid); CHECK_SIZE_AND_OFFSET(passwd, pw_gid); CHECK_SIZE_AND_OFFSET(passwd, pw_dir); CHECK_SIZE_AND_OFFSET(passwd, pw_shell); #if !SANITIZER_ANDROID CHECK_SIZE_AND_OFFSET(passwd, pw_gecos); #endif #if SANITIZER_MAC CHECK_SIZE_AND_OFFSET(passwd, pw_change); CHECK_SIZE_AND_OFFSET(passwd, pw_expire); CHECK_SIZE_AND_OFFSET(passwd, pw_class); #endif CHECK_TYPE_SIZE(group); CHECK_SIZE_AND_OFFSET(group, gr_name); CHECK_SIZE_AND_OFFSET(group, gr_passwd); CHECK_SIZE_AND_OFFSET(group, gr_gid); CHECK_SIZE_AND_OFFSET(group, gr_mem); #if SANITIZER_LINUX && !SANITIZER_ANDROID CHECK_TYPE_SIZE(XDR); CHECK_SIZE_AND_OFFSET(XDR, x_op); CHECK_SIZE_AND_OFFSET(XDR, x_ops); CHECK_SIZE_AND_OFFSET(XDR, x_public); CHECK_SIZE_AND_OFFSET(XDR, x_private); CHECK_SIZE_AND_OFFSET(XDR, x_base); CHECK_SIZE_AND_OFFSET(XDR, x_handy); COMPILER_CHECK(__sanitizer_XDR_ENCODE == XDR_ENCODE); COMPILER_CHECK(__sanitizer_XDR_DECODE == XDR_DECODE); COMPILER_CHECK(__sanitizer_XDR_FREE == XDR_FREE); #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID COMPILER_CHECK(sizeof(__sanitizer_FILE) <= sizeof(FILE)); CHECK_SIZE_AND_OFFSET(FILE, _flags); CHECK_SIZE_AND_OFFSET(FILE, _IO_read_ptr); CHECK_SIZE_AND_OFFSET(FILE, _IO_read_end); CHECK_SIZE_AND_OFFSET(FILE, _IO_read_base); CHECK_SIZE_AND_OFFSET(FILE, _IO_write_ptr); CHECK_SIZE_AND_OFFSET(FILE, _IO_write_end); CHECK_SIZE_AND_OFFSET(FILE, _IO_write_base); CHECK_SIZE_AND_OFFSET(FILE, _IO_buf_base); CHECK_SIZE_AND_OFFSET(FILE, _IO_buf_end); CHECK_SIZE_AND_OFFSET(FILE, _IO_save_base); CHECK_SIZE_AND_OFFSET(FILE, _IO_backup_base); CHECK_SIZE_AND_OFFSET(FILE, _IO_save_end); CHECK_SIZE_AND_OFFSET(FILE, _markers); CHECK_SIZE_AND_OFFSET(FILE, _chain); CHECK_SIZE_AND_OFFSET(FILE, _fileno); #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID COMPILER_CHECK(sizeof(__sanitizer__obstack_chunk) <= sizeof(_obstack_chunk)); CHECK_SIZE_AND_OFFSET(_obstack_chunk, limit); CHECK_SIZE_AND_OFFSET(_obstack_chunk, prev); CHECK_TYPE_SIZE(obstack); CHECK_SIZE_AND_OFFSET(obstack, chunk_size); CHECK_SIZE_AND_OFFSET(obstack, chunk); CHECK_SIZE_AND_OFFSET(obstack, object_base); CHECK_SIZE_AND_OFFSET(obstack, next_free); #endif #endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC Index: projects/ifnet/contrib/compiler-rt =================================================================== --- projects/ifnet/contrib/compiler-rt (revision 277209) +++ projects/ifnet/contrib/compiler-rt (revision 277210) Property changes on: projects/ifnet/contrib/compiler-rt ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/contrib/compiler-rt:r277094-277209 Index: projects/ifnet/contrib/elftoolchain/elfcopy/sections.c =================================================================== --- projects/ifnet/contrib/elftoolchain/elfcopy/sections.c (revision 277209) +++ projects/ifnet/contrib/elftoolchain/elfcopy/sections.c (revision 277210) @@ -1,1548 +1,1550 @@ /*- * Copyright (c) 2007-2011,2014 Kai Wang * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include "elfcopy.h" ELFTC_VCSID("$Id: sections.c 3134 2014-12-23 10:43:59Z kaiwang27 $"); static void add_gnu_debuglink(struct elfcopy *ecp); static uint32_t calc_crc32(const char *p, size_t len, uint32_t crc); static void check_section_rename(struct elfcopy *ecp, struct section *s); static void filter_reloc(struct elfcopy *ecp, struct section *s); static int get_section_flags(struct elfcopy *ecp, const char *name); static void insert_sections(struct elfcopy *ecp); static void insert_to_strtab(struct section *t, const char *s); static int is_append_section(struct elfcopy *ecp, const char *name); static int is_compress_section(struct elfcopy *ecp, const char *name); static int is_debug_section(const char *name); static int is_modify_section(struct elfcopy *ecp, const char *name); static int is_print_section(struct elfcopy *ecp, const char *name); static int lookup_string(struct section *t, const char *s); static void modify_section(struct elfcopy *ecp, struct section *s); static void pad_section(struct elfcopy *ecp, struct section *s); static void print_data(const char *d, size_t sz); static void print_section(struct section *s); static void *read_section(struct section *s, size_t *size); static void update_reloc(struct elfcopy *ecp, struct section *s); int is_remove_section(struct elfcopy *ecp, const char *name) { /* Always keep section name table */ if (strcmp(name, ".shstrtab") == 0) return 0; if (strcmp(name, ".symtab") == 0 || strcmp(name, ".strtab") == 0) { if (ecp->strip == STRIP_ALL && lookup_symop_list( ecp, NULL, SYMOP_KEEP) == NULL) return (1); else return (0); } if (is_debug_section(name)) { if (ecp->strip == STRIP_ALL || ecp->strip == STRIP_DEBUG || ecp->strip == STRIP_UNNEEDED || (ecp->flags & DISCARD_LOCAL)) return (1); if (ecp->strip == STRIP_NONDEBUG) return (0); } if ((ecp->flags & SEC_REMOVE) || (ecp->flags & SEC_COPY)) { struct sec_action *sac; sac = lookup_sec_act(ecp, name, 0); if ((ecp->flags & SEC_REMOVE) && sac != NULL && sac->remove) return (1); if ((ecp->flags & SEC_COPY) && (sac == NULL || !sac->copy)) return (1); } return (0); } /* * Relocation section needs to be removed if the section it applies to * will be removed. */ int is_remove_reloc_sec(struct elfcopy *ecp, uint32_t sh_info) { const char *name; GElf_Shdr ish; Elf_Scn *is; size_t indx; int elferr; if (elf_getshstrndx(ecp->ein, &indx) == 0) errx(EXIT_FAILURE, "elf_getshstrndx failed: %s", elf_errmsg(-1)); is = NULL; while ((is = elf_nextscn(ecp->ein, is)) != NULL) { if (sh_info == elf_ndxscn(is)) { if (gelf_getshdr(is, &ish) == NULL) errx(EXIT_FAILURE, "gelf_getshdr failed: %s", elf_errmsg(-1)); if ((name = elf_strptr(ecp->ein, indx, ish.sh_name)) == NULL) errx(EXIT_FAILURE, "elf_strptr failed: %s", elf_errmsg(-1)); if (is_remove_section(ecp, name)) return (1); else return (0); } } elferr = elf_errno(); if (elferr != 0) errx(EXIT_FAILURE, "elf_nextscn failed: %s", elf_errmsg(elferr)); /* Remove reloc section if we can't find the target section. */ return (1); } static int is_append_section(struct elfcopy *ecp, const char *name) { struct sec_action *sac; sac = lookup_sec_act(ecp, name, 0); if (sac != NULL && sac->append != 0 && sac->string != NULL) return (1); return (0); } static int is_compress_section(struct elfcopy *ecp, const char *name) { struct sec_action *sac; sac = lookup_sec_act(ecp, name, 0); if (sac != NULL && sac->compress != 0) return (1); return (0); } static void check_section_rename(struct elfcopy *ecp, struct section *s) { struct sec_action *sac; char *prefix; size_t namelen; if (s->pseudo) return; sac = lookup_sec_act(ecp, s->name, 0); if (sac != NULL && sac->rename) s->name = sac->newname; if (!strcmp(s->name, ".symtab") || !strcmp(s->name, ".strtab") || !strcmp(s->name, ".shstrtab")) return; prefix = NULL; if (s->loadable && ecp->prefix_alloc != NULL) prefix = ecp->prefix_alloc; else if (ecp->prefix_sec != NULL) prefix = ecp->prefix_sec; if (prefix != NULL) { namelen = strlen(s->name) + strlen(prefix) + 1; if ((s->newname = malloc(namelen)) == NULL) err(EXIT_FAILURE, "malloc failed"); snprintf(s->newname, namelen, "%s%s", prefix, s->name); s->name = s->newname; } } static int get_section_flags(struct elfcopy *ecp, const char *name) { struct sec_action *sac; sac = lookup_sec_act(ecp, name, 0); if (sac != NULL && sac->flags) return sac->flags; return (0); } /* * Determine whether the section are debugging section. * According to libbfd, debugging sections are recognized * only by name. */ static int is_debug_section(const char *name) { const char *dbg_sec[] = { ".debug", ".gnu.linkonce.wi.", ".line", ".stab", NULL }; const char **p; for(p = dbg_sec; *p; p++) { if (strncmp(name, *p, strlen(*p)) == 0) return (1); } return (0); } static int is_print_section(struct elfcopy *ecp, const char *name) { struct sec_action *sac; sac = lookup_sec_act(ecp, name, 0); if (sac != NULL && sac->print != 0) return (1); return (0); } static int is_modify_section(struct elfcopy *ecp, const char *name) { if (is_append_section(ecp, name) || is_compress_section(ecp, name)) return (1); return (0); } struct sec_action* lookup_sec_act(struct elfcopy *ecp, const char *name, int add) { struct sec_action *sac; if (name == NULL) return NULL; STAILQ_FOREACH(sac, &ecp->v_sac, sac_list) { if (strcmp(name, sac->name) == 0) return sac; } if (add == 0) return NULL; if ((sac = malloc(sizeof(*sac))) == NULL) errx(EXIT_FAILURE, "not enough memory"); memset(sac, 0, sizeof(*sac)); sac->name = name; STAILQ_INSERT_TAIL(&ecp->v_sac, sac, sac_list); return (sac); } void free_sec_act(struct elfcopy *ecp) { struct sec_action *sac, *sac_temp; STAILQ_FOREACH_SAFE(sac, &ecp->v_sac, sac_list, sac_temp) { STAILQ_REMOVE(&ecp->v_sac, sac, sec_action, sac_list); free(sac); } } void insert_to_sec_list(struct elfcopy *ecp, struct section *sec, int tail) { struct section *s; if (!tail) { TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { if (sec->off < s->off) { TAILQ_INSERT_BEFORE(s, sec, sec_list); goto inc_nos; } } } TAILQ_INSERT_TAIL(&ecp->v_sec, sec, sec_list); inc_nos: if (sec->pseudo == 0) ecp->nos++; } /* * First step of section creation: create scn and internal section * structure, discard sections to be removed. */ void create_scn(struct elfcopy *ecp) { struct section *s; const char *name; Elf_Scn *is; GElf_Shdr ish; size_t indx; uint64_t oldndx, newndx; int elferr, sec_flags; /* * Insert a pseudo section that contains the ELF header * and program header. Used as reference for section offset * or load address adjustment. */ if ((s = calloc(1, sizeof(*s))) == NULL) err(EXIT_FAILURE, "calloc failed"); s->off = 0; s->sz = gelf_fsize(ecp->eout, ELF_T_EHDR, 1, EV_CURRENT) + gelf_fsize(ecp->eout, ELF_T_PHDR, ecp->ophnum, EV_CURRENT); s->align = 1; s->pseudo = 1; s->loadable = add_to_inseg_list(ecp, s); insert_to_sec_list(ecp, s, 0); /* Create internal .shstrtab section. */ init_shstrtab(ecp); if (elf_getshstrndx(ecp->ein, &indx) == 0) errx(EXIT_FAILURE, "elf_getshstrndx failed: %s", elf_errmsg(-1)); is = NULL; while ((is = elf_nextscn(ecp->ein, is)) != NULL) { if (gelf_getshdr(is, &ish) == NULL) errx(EXIT_FAILURE, "219 gelf_getshdr failed: %s", elf_errmsg(-1)); if ((name = elf_strptr(ecp->ein, indx, ish.sh_name)) == NULL) errx(EXIT_FAILURE, "elf_strptr failed: %s", elf_errmsg(-1)); /* Skip sections to be removed. */ if (is_remove_section(ecp, name)) continue; /* * Relocation section need to be remove if the section * it applies will be removed. */ if (ish.sh_type == SHT_REL || ish.sh_type == SHT_RELA) if (ish.sh_info != 0 && is_remove_reloc_sec(ecp, ish.sh_info)) continue; /* * Section groups should be removed if symbol table will * be removed. (section group's signature stored in symbol * table) */ if (ish.sh_type == SHT_GROUP && ecp->strip == STRIP_ALL) continue; /* Get section flags set by user. */ sec_flags = get_section_flags(ecp, name); /* Create internal section object. */ if (strcmp(name, ".shstrtab") != 0) { if ((s = calloc(1, sizeof(*s))) == NULL) err(EXIT_FAILURE, "calloc failed"); s->name = name; s->is = is; s->off = ish.sh_offset; s->sz = ish.sh_size; s->align = ish.sh_addralign; s->type = ish.sh_type; s->vma = ish.sh_addr; /* * Search program headers to determine whether section * is loadable, but if user explicitly set section flags * while neither "load" nor "alloc" is set, we make the * section unloadable. */ if (sec_flags && (sec_flags & (SF_LOAD | SF_ALLOC)) == 0) s->loadable = 0; else s->loadable = add_to_inseg_list(ecp, s); } else { /* Assuming .shstrtab is "unloadable". */ s = ecp->shstrtab; s->off = ish.sh_offset; } oldndx = newndx = SHN_UNDEF; if (strcmp(name, ".symtab") != 0 && strcmp(name, ".strtab") != 0) { if (!strcmp(name, ".shstrtab")) { /* * Add sections specified by --add-section and * gnu debuglink. we want these sections have * smaller index than .shstrtab section. */ if (ecp->debuglink != NULL) add_gnu_debuglink(ecp); if (ecp->flags & SEC_ADD) insert_sections(ecp); } if ((s->os = elf_newscn(ecp->eout)) == NULL) errx(EXIT_FAILURE, "elf_newscn failed: %s", elf_errmsg(-1)); if ((newndx = elf_ndxscn(s->os)) == SHN_UNDEF) errx(EXIT_FAILURE, "elf_ndxscn failed: %s", elf_errmsg(-1)); } if ((oldndx = elf_ndxscn(is)) == SHN_UNDEF) errx(EXIT_FAILURE, "elf_ndxscn failed: %s", elf_errmsg(-1)); if (oldndx != SHN_UNDEF && newndx != SHN_UNDEF) ecp->secndx[oldndx] = newndx; /* * If strip action is STRIP_NONDEBUG(only keep debug), * change sections flags of loadable sections to SHF_NOBITS, * and the content of those sections will be ignored. */ if (ecp->strip == STRIP_NONDEBUG && (ish.sh_flags & SHF_ALLOC)) s->type = SHT_NOBITS; check_section_rename(ecp, s); /* create section header based on input object. */ if (strcmp(name, ".symtab") != 0 && strcmp(name, ".strtab") != 0 && strcmp(name, ".shstrtab") != 0) copy_shdr(ecp, s, NULL, 0, sec_flags); if (strcmp(name, ".symtab") == 0) { ecp->flags |= SYMTAB_EXIST; ecp->symtab = s; } if (strcmp(name, ".strtab") == 0) ecp->strtab = s; insert_to_sec_list(ecp, s, 0); } elferr = elf_errno(); if (elferr != 0) errx(EXIT_FAILURE, "elf_nextscn failed: %s", elf_errmsg(elferr)); } struct section * insert_shtab(struct elfcopy *ecp, int tail) { struct section *s, *shtab; GElf_Ehdr ieh; int nsecs; /* * Treat section header table as a "pseudo" section, insert it * into section list, so later it will get sorted and resynced * just as normal sections. */ if ((shtab = calloc(1, sizeof(*shtab))) == NULL) errx(EXIT_FAILURE, "calloc failed"); if (!tail) { /* * "shoff" of input object is used as a hint for section * resync later. */ if (gelf_getehdr(ecp->ein, &ieh) == NULL) errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", elf_errmsg(-1)); shtab->off = ieh.e_shoff; } else shtab->off = 0; /* Calculate number of sections in the output object. */ nsecs = 0; TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { if (!s->pseudo) nsecs++; } /* Remember there is always a null section, so we +1 here. */ shtab->sz = gelf_fsize(ecp->eout, ELF_T_SHDR, nsecs + 1, EV_CURRENT); if (shtab->sz == 0) errx(EXIT_FAILURE, "gelf_fsize() failed: %s", elf_errmsg(-1)); shtab->align = (ecp->oec == ELFCLASS32 ? 4 : 8); shtab->loadable = 0; shtab->pseudo = 1; insert_to_sec_list(ecp, shtab, tail); return (shtab); } void copy_content(struct elfcopy *ecp) { struct section *s; TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { /* Skip pseudo section. */ if (s->pseudo) continue; /* Skip special sections. */ if (strcmp(s->name, ".symtab") == 0 || strcmp(s->name, ".strtab") == 0 || strcmp(s->name, ".shstrtab") == 0) continue; /* * If strip action is STRIP_ALL, relocation info need * to be stripped. Skip filtering otherwisw. */ if (ecp->strip == STRIP_ALL && (s->type == SHT_REL || s->type == SHT_RELA)) filter_reloc(ecp, s); if (is_modify_section(ecp, s->name)) modify_section(ecp, s); copy_data(s); /* * If symbol table is modified, relocation info might * need update, as symbol index may have changed. */ if ((ecp->flags & SYMTAB_INTACT) == 0 && (ecp->flags & SYMTAB_EXIST) && (s->type == SHT_REL || s->type == SHT_RELA)) update_reloc(ecp, s); if (is_print_section(ecp, s->name)) print_section(s); } } /* * Filter relocation entries, only keep those entries whose * symbol is in the keep list. */ static void filter_reloc(struct elfcopy *ecp, struct section *s) { const char *name; GElf_Shdr ish; GElf_Rel rel; GElf_Rela rela; Elf32_Rel *rel32; Elf64_Rel *rel64; Elf32_Rela *rela32; Elf64_Rela *rela64; Elf_Data *id; uint64_t cap, n, nrels; int elferr, i; if (gelf_getshdr(s->is, &ish) == NULL) errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", elf_errmsg(-1)); /* We don't want to touch relocation info for dynamic symbols. */ if ((ecp->flags & SYMTAB_EXIST) == 0) { if (ish.sh_link == 0 || ecp->secndx[ish.sh_link] == 0) { /* * This reloc section applies to the symbol table * that was stripped, so discard whole section. */ s->nocopy = 1; s->sz = 0; } return; } else { /* Symbol table exist, check if index equals. */ if (ish.sh_link != elf_ndxscn(ecp->symtab->is)) return; } #define COPYREL(REL, SZ) do { \ if (nrels == 0) { \ if ((REL##SZ = malloc(cap * \ sizeof(Elf##SZ##_Rel))) == NULL) \ err(EXIT_FAILURE, "malloc failed"); \ } \ if (nrels >= cap) { \ cap *= 2; \ if ((REL##SZ = realloc(REL##SZ, cap * \ sizeof(Elf##SZ##_Rel))) == NULL) \ err(EXIT_FAILURE, "realloc failed"); \ } \ REL##SZ[nrels].r_offset = REL.r_offset; \ REL##SZ[nrels].r_info = REL.r_info; \ if (s->type == SHT_RELA) \ rela##SZ[nrels].r_addend = rela.r_addend; \ nrels++; \ } while (0) nrels = 0; cap = 4; /* keep list is usually small. */ rel32 = NULL; rel64 = NULL; rela32 = NULL; rela64 = NULL; if ((id = elf_getdata(s->is, NULL)) == NULL) errx(EXIT_FAILURE, "elf_getdata() failed: %s", elf_errmsg(-1)); n = ish.sh_size / ish.sh_entsize; for(i = 0; (uint64_t)i < n; i++) { if (s->type == SHT_REL) { if (gelf_getrel(id, i, &rel) != &rel) errx(EXIT_FAILURE, "gelf_getrel failed: %s", elf_errmsg(-1)); } else { if (gelf_getrela(id, i, &rela) != &rela) errx(EXIT_FAILURE, "gelf_getrel failed: %s", elf_errmsg(-1)); } name = elf_strptr(ecp->ein, elf_ndxscn(ecp->strtab->is), GELF_R_SYM(rel.r_info)); if (name == NULL) errx(EXIT_FAILURE, "elf_strptr failed: %s", elf_errmsg(-1)); if (lookup_symop_list(ecp, name, SYMOP_KEEP) != NULL) { if (ecp->oec == ELFCLASS32) { if (s->type == SHT_REL) COPYREL(rel, 32); else COPYREL(rela, 32); } else { if (s->type == SHT_REL) COPYREL(rel, 64); else COPYREL(rela, 64); } } } elferr = elf_errno(); if (elferr != 0) errx(EXIT_FAILURE, "elf_getdata() failed: %s", elf_errmsg(elferr)); if (ecp->oec == ELFCLASS32) { if (s->type == SHT_REL) s->buf = rel32; else s->buf = rela32; } else { if (s->type == SHT_REL) s->buf = rel64; else s->buf = rela64; } s->sz = gelf_fsize(ecp->eout, (s->type == SHT_REL ? ELF_T_REL : ELF_T_RELA), nrels, EV_CURRENT); s->nocopy = 1; } static void update_reloc(struct elfcopy *ecp, struct section *s) { GElf_Shdr osh; GElf_Rel rel; GElf_Rela rela; Elf_Data *od; uint64_t n; int i; #define UPDATEREL(REL) do { \ if (gelf_get##REL(od, i, &REL) != &REL) \ errx(EXIT_FAILURE, "gelf_get##REL failed: %s", \ elf_errmsg(-1)); \ REL.r_info = GELF_R_INFO(ecp->symndx[GELF_R_SYM(REL.r_info)], \ GELF_R_TYPE(REL.r_info)); \ if (!gelf_update_##REL(od, i, &REL)) \ errx(EXIT_FAILURE, "gelf_update_##REL failed: %s", \ elf_errmsg(-1)); \ } while(0) if (s->sz == 0) return; if (gelf_getshdr(s->os, &osh) == NULL) errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", elf_errmsg(-1)); /* Only process .symtab reloc info. */ if (osh.sh_link != elf_ndxscn(ecp->symtab->is)) return; if ((od = elf_getdata(s->os, NULL)) == NULL) errx(EXIT_FAILURE, "elf_getdata() failed: %s", elf_errmsg(-1)); n = osh.sh_size / osh.sh_entsize; for(i = 0; (uint64_t)i < n; i++) { if (s->type == SHT_REL) UPDATEREL(rel); else UPDATEREL(rela); } } static void pad_section(struct elfcopy *ecp, struct section *s) { GElf_Shdr osh; Elf_Data *od; if (s == NULL || s->pad_sz == 0) return; if ((s->pad = malloc(s->pad_sz)) == NULL) err(EXIT_FAILURE, "malloc failed"); memset(s->pad, ecp->fill, s->pad_sz); /* Create a new Elf_Data to contain the padding bytes. */ if ((od = elf_newdata(s->os)) == NULL) errx(EXIT_FAILURE, "elf_newdata() failed: %s", elf_errmsg(-1)); od->d_align = 1; od->d_off = s->sz; od->d_buf = s->pad; od->d_type = ELF_T_BYTE; od->d_size = s->pad_sz; od->d_version = EV_CURRENT; /* Update section header. */ if (gelf_getshdr(s->os, &osh) == NULL) errx(EXIT_FAILURE, "gelf_getshdr() failed: %s", elf_errmsg(-1)); osh.sh_size = s->sz + s->pad_sz; if (!gelf_update_shdr(s->os, &osh)) errx(EXIT_FAILURE, "elf_update_shdr failed: %s", elf_errmsg(-1)); } void resync_sections(struct elfcopy *ecp) { struct section *s, *ps; GElf_Shdr osh; uint64_t off; int first; ps = NULL; first = 1; off = 0; TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { if (first) { off = s->off; first = 0; } /* * Ignore TLS sections with load address 0 and without * content. We don't need to adjust their file offset or * VMA, only the size matters. */ if (s->seg_tls != NULL && s->type == SHT_NOBITS && s->off == 0) continue; /* Align section offset. */ + if (s->align == 0) + s->align = 1; if (off <= s->off) { if (!s->loadable) s->off = roundup(off, s->align); } else { if (s->loadable) warnx("moving loadable section %s, " "is this intentional?", s->name); s->off = roundup(off, s->align); } /* Calculate next section offset. */ off = s->off; if (s->pseudo || (s->type != SHT_NOBITS && s->type != SHT_NULL)) off += s->sz; if (s->pseudo) { ps = NULL; continue; } /* Count padding bytes added through --pad-to. */ if (s->pad_sz > 0) off += s->pad_sz; /* Update section header accordingly. */ if (gelf_getshdr(s->os, &osh) == NULL) errx(EXIT_FAILURE, "gelf_getshdr() failed: %s", elf_errmsg(-1)); osh.sh_addr = s->vma; osh.sh_offset = s->off; osh.sh_size = s->sz; if (!gelf_update_shdr(s->os, &osh)) errx(EXIT_FAILURE, "elf_update_shdr failed: %s", elf_errmsg(-1)); /* Add padding for previous section, if need. */ if (ps != NULL) { if (ps->pad_sz > 0) { /* Apply padding added by --pad-to. */ pad_section(ecp, ps); } else if ((ecp->flags & GAP_FILL) && (ps->off + ps->sz < s->off)) { /* * Fill the gap between sections by padding * the section with lower address. */ ps->pad_sz = s->off - (ps->off + ps->sz); pad_section(ecp, ps); } } ps = s; } /* Pad the last section, if need. */ if (ps != NULL && ps->pad_sz > 0) pad_section(ecp, ps); } static void modify_section(struct elfcopy *ecp, struct section *s) { struct sec_action *sac; size_t srcsz, dstsz, p, len; char *b, *c, *d, *src, *end; int dupe; src = read_section(s, &srcsz); if (src == NULL || srcsz == 0) { /* For empty section, we proceed if we need to append. */ if (!is_append_section(ecp, s->name)) return; } /* Allocate buffer needed for new section data. */ dstsz = srcsz; if (is_append_section(ecp, s->name)) { sac = lookup_sec_act(ecp, s->name, 0); dstsz += strlen(sac->string) + 1; } if ((b = malloc(dstsz)) == NULL) err(EXIT_FAILURE, "malloc failed"); s->buf = b; /* Compress section. */ p = 0; if (is_compress_section(ecp, s->name)) { end = src + srcsz; for(c = src; c < end;) { len = 0; while(c + len < end && c[len] != '\0') len++; if (c + len == end) { /* XXX should we warn here? */ strncpy(&b[p], c, len); p += len; break; } dupe = 0; for (d = b; d < b + p; ) { if (strcmp(d, c) == 0) { dupe = 1; break; } d += strlen(d) + 1; } if (!dupe) { strncpy(&b[p], c, len); b[p + len] = '\0'; p += len + 1; } c += len + 1; } } else { memcpy(b, src, srcsz); p += srcsz; } /* Append section. */ if (is_append_section(ecp, s->name)) { sac = lookup_sec_act(ecp, s->name, 0); len = strlen(sac->string); strncpy(&b[p], sac->string, len); b[p + len] = '\0'; p += len + 1; } s->sz = p; s->nocopy = 1; } static void print_data(const char *d, size_t sz) { const char *c; for (c = d; c < d + sz; c++) { if (*c == '\0') putchar('\n'); else putchar(*c); } } static void print_section(struct section *s) { Elf_Data *id; int elferr; if (s->buf != NULL && s->sz > 0) { print_data(s->buf, s->sz); } else { id = NULL; while ((id = elf_getdata(s->is, id)) != NULL) print_data(id->d_buf, id->d_size); elferr = elf_errno(); if (elferr != 0) errx(EXIT_FAILURE, "elf_getdata() failed: %s", elf_errmsg(elferr)); } putchar('\n'); } static void * read_section(struct section *s, size_t *size) { Elf_Data *id; char *b; size_t sz; int elferr; sz = 0; b = NULL; id = NULL; while ((id = elf_getdata(s->is, id)) != NULL) { if (b == NULL) b = malloc(id->d_size); else b = malloc(sz + id->d_size); if (b == NULL) err(EXIT_FAILURE, "malloc or realloc failed"); memcpy(&b[sz], id->d_buf, id->d_size); sz += id->d_size; } elferr = elf_errno(); if (elferr != 0) errx(EXIT_FAILURE, "elf_getdata() failed: %s", elf_errmsg(elferr)); *size = sz; return (b); } void copy_shdr(struct elfcopy *ecp, struct section *s, const char *name, int copy, int sec_flags) { GElf_Shdr ish, osh; if (gelf_getshdr(s->is, &ish) == NULL) errx(EXIT_FAILURE, "526 gelf_getshdr() failed: %s", elf_errmsg(-1)); if (gelf_getshdr(s->os, &osh) == NULL) errx(EXIT_FAILURE, "529 gelf_getshdr() failed: %s", elf_errmsg(-1)); if (copy) (void) memcpy(&osh, &ish, sizeof(ish)); else { osh.sh_type = s->type; osh.sh_addr = s->vma; osh.sh_offset = s->off; osh.sh_size = s->sz; osh.sh_link = ish.sh_link; osh.sh_info = ish.sh_info; osh.sh_addralign = s->align; osh.sh_entsize = ish.sh_entsize; if (sec_flags) { osh.sh_flags = 0; if (sec_flags & SF_ALLOC) { osh.sh_flags |= SHF_ALLOC; if (!s->loadable) warnx("set SHF_ALLOC flag for " "unloadable section %s", s->name); } if ((sec_flags & SF_READONLY) == 0) osh.sh_flags |= SHF_WRITE; if (sec_flags & SF_CODE) osh.sh_flags |= SHF_EXECINSTR; } else osh.sh_flags = ish.sh_flags; } if (name == NULL) add_to_shstrtab(ecp, s->name); else add_to_shstrtab(ecp, name); if (!gelf_update_shdr(s->os, &osh)) errx(EXIT_FAILURE, "elf_update_shdr failed: %s", elf_errmsg(-1)); } void copy_data(struct section *s) { Elf_Data *id, *od; int elferr; if (s->nocopy && s->buf == NULL) return; if ((id = elf_getdata(s->is, NULL)) == NULL) { elferr = elf_errno(); if (elferr != 0) errx(EXIT_FAILURE, "elf_getdata() failed: %s", elf_errmsg(elferr)); return; } if ((od = elf_newdata(s->os)) == NULL) errx(EXIT_FAILURE, "elf_newdata() failed: %s", elf_errmsg(-1)); if (s->nocopy) { /* Use s->buf as content if s->nocopy is set. */ od->d_align = id->d_align; od->d_off = 0; od->d_buf = s->buf; od->d_type = id->d_type; od->d_size = s->sz; od->d_version = id->d_version; } else { od->d_align = id->d_align; od->d_off = id->d_off; od->d_buf = id->d_buf; od->d_type = id->d_type; od->d_size = id->d_size; od->d_version = id->d_version; } /* * Alignment Fixup. libelf does not allow the alignment for * Elf_Data descriptor to be set to 0. In this case we workaround * it by setting the alignment to 1. * * According to the ELF ABI, alignment 0 and 1 has the same * meaning: the section has no alignment constraints. */ if (od->d_align == 0) od->d_align = 1; } struct section * create_external_section(struct elfcopy *ecp, const char *name, char *newname, void *buf, uint64_t size, uint64_t off, uint64_t stype, Elf_Type dtype, uint64_t flags, uint64_t align, uint64_t vma, int loadable) { struct section *s; Elf_Scn *os; Elf_Data *od; GElf_Shdr osh; if ((os = elf_newscn(ecp->eout)) == NULL) errx(EXIT_FAILURE, "elf_newscn() failed: %s", elf_errmsg(-1)); if ((s = calloc(1, sizeof(*s))) == NULL) err(EXIT_FAILURE, "calloc failed"); s->name = name; s->newname = newname; /* needs to be free()'ed */ s->off = off; s->sz = size; s->vma = vma; s->align = align; s->loadable = loadable; s->is = NULL; s->os = os; s->type = stype; s->nocopy = 1; insert_to_sec_list(ecp, s, 1); if (gelf_getshdr(os, &osh) == NULL) errx(EXIT_FAILURE, "gelf_getshdr() failed: %s", elf_errmsg(-1)); osh.sh_flags = flags; osh.sh_type = s->type; osh.sh_addr = s->vma; osh.sh_addralign = s->align; if (!gelf_update_shdr(os, &osh)) errx(EXIT_FAILURE, "gelf_update_shdr() failed: %s", elf_errmsg(-1)); add_to_shstrtab(ecp, name); if (buf != NULL && size != 0) { if ((od = elf_newdata(os)) == NULL) errx(EXIT_FAILURE, "elf_newdata() failed: %s", elf_errmsg(-1)); od->d_align = align; od->d_off = 0; od->d_buf = buf; od->d_size = size; od->d_type = dtype; od->d_version = EV_CURRENT; } /* * Clear SYMTAB_INTACT, as we probably need to update/add new * STT_SECTION symbols into the symbol table. */ ecp->flags &= ~SYMTAB_INTACT; return (s); } /* * Insert sections specified by --add-section to the end of section list. */ static void insert_sections(struct elfcopy *ecp) { struct sec_add *sa; struct section *s; size_t off; /* Put these sections in the end of current list. */ off = 0; TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { if (s->type != SHT_NOBITS && s->type != SHT_NULL) off = s->off + s->sz; else off = s->off; } STAILQ_FOREACH(sa, &ecp->v_sadd, sadd_list) { /* TODO: Add section header vma/lma, flag changes here */ (void) create_external_section(ecp, sa->name, NULL, sa->content, sa->size, off, SHT_PROGBITS, ELF_T_BYTE, 0, 1, 0, 0); } } void add_to_shstrtab(struct elfcopy *ecp, const char *name) { struct section *s; s = ecp->shstrtab; insert_to_strtab(s, name); } void update_shdr(struct elfcopy *ecp, int update_link) { struct section *s; GElf_Shdr osh; int elferr; TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { if (s->pseudo) continue; if (gelf_getshdr(s->os, &osh) == NULL) errx(EXIT_FAILURE, "668 gelf_getshdr failed: %s", elf_errmsg(-1)); /* Find section name in string table and set sh_name. */ osh.sh_name = lookup_string(ecp->shstrtab, s->name); /* * sh_link needs to be updated, since the index of the * linked section might have changed. */ if (update_link && osh.sh_link != 0) osh.sh_link = ecp->secndx[osh.sh_link]; /* * sh_info of relocation section links to the section to which * its relocation info applies. So it may need update as well. */ if ((s->type == SHT_REL || s->type == SHT_RELA) && osh.sh_info != 0) osh.sh_info = ecp->secndx[osh.sh_info]; if (!gelf_update_shdr(s->os, &osh)) errx(EXIT_FAILURE, "gelf_update_shdr() failed: %s", elf_errmsg(-1)); } elferr = elf_errno(); if (elferr != 0) errx(EXIT_FAILURE, "elf_nextscn failed: %s", elf_errmsg(elferr)); } void init_shstrtab(struct elfcopy *ecp) { struct section *s; if ((ecp->shstrtab = calloc(1, sizeof(*ecp->shstrtab))) == NULL) err(EXIT_FAILURE, "calloc failed"); s = ecp->shstrtab; s->name = ".shstrtab"; s->is = NULL; s->sz = 0; s->align = 1; s->loadable = 0; s->type = SHT_STRTAB; s->vma = 0; insert_to_strtab(s, ""); insert_to_strtab(s, ".symtab"); insert_to_strtab(s, ".strtab"); insert_to_strtab(s, ".shstrtab"); } void set_shstrtab(struct elfcopy *ecp) { struct section *s; Elf_Data *data; GElf_Shdr sh; s = ecp->shstrtab; if (gelf_getshdr(s->os, &sh) == NULL) errx(EXIT_FAILURE, "692 gelf_getshdr() failed: %s", elf_errmsg(-1)); sh.sh_addr = 0; sh.sh_addralign = 1; sh.sh_offset = s->off; sh.sh_type = SHT_STRTAB; sh.sh_flags = 0; sh.sh_entsize = 0; sh.sh_info = 0; sh.sh_link = 0; if ((data = elf_newdata(s->os)) == NULL) errx(EXIT_FAILURE, "elf_newdata() failed: %s", elf_errmsg(-1)); /* * If we don't have a symbol table, skip those a few bytes * which are reserved for this in the beginning of shstrtab. */ if (!(ecp->flags & SYMTAB_EXIST)) { s->sz -= sizeof(".symtab\0.strtab"); memmove(s->buf, (char *)s->buf + sizeof(".symtab\0.strtab"), s->sz); } sh.sh_size = s->sz; if (!gelf_update_shdr(s->os, &sh)) errx(EXIT_FAILURE, "gelf_update_shdr() failed: %s", elf_errmsg(-1)); data->d_align = 1; data->d_buf = s->buf; data->d_size = s->sz; data->d_off = 0; data->d_type = ELF_T_BYTE; data->d_version = EV_CURRENT; if (!elf_setshstrndx(ecp->eout, elf_ndxscn(s->os))) errx(EXIT_FAILURE, "elf_setshstrndx() failed: %s", elf_errmsg(-1)); } void add_section(struct elfcopy *ecp, const char *arg) { struct sec_add *sa; struct stat sb; const char *s, *fn; FILE *fp; int len; if ((s = strchr(arg, '=')) == NULL) errx(EXIT_FAILURE, "illegal format for --add-section option"); if ((sa = malloc(sizeof(*sa))) == NULL) err(EXIT_FAILURE, "malloc failed"); len = s - arg; if ((sa->name = malloc(len + 1)) == NULL) err(EXIT_FAILURE, "malloc failed"); strncpy(sa->name, arg, len); sa->name[len] = '\0'; fn = s + 1; if (stat(fn, &sb) == -1) err(EXIT_FAILURE, "stat failed"); sa->size = sb.st_size; if ((sa->content = malloc(sa->size)) == NULL) err(EXIT_FAILURE, "malloc failed"); if ((fp = fopen(fn, "r")) == NULL) err(EXIT_FAILURE, "can not open %s", fn); if (fread(sa->content, 1, sa->size, fp) == 0 || ferror(fp)) err(EXIT_FAILURE, "fread failed"); fclose(fp); STAILQ_INSERT_TAIL(&ecp->v_sadd, sa, sadd_list); ecp->flags |= SEC_ADD; } void free_sec_add(struct elfcopy *ecp) { struct sec_add *sa, *sa_temp; STAILQ_FOREACH_SAFE(sa, &ecp->v_sadd, sadd_list, sa_temp) { STAILQ_REMOVE(&ecp->v_sadd, sa, sec_add, sadd_list); free(sa->name); free(sa->content); free(sa); } } static void add_gnu_debuglink(struct elfcopy *ecp) { struct sec_add *sa; struct stat sb; FILE *fp; char *fnbase, *buf; int crc_off; int crc; if (ecp->debuglink == NULL) return; /* Read debug file content. */ if ((sa = malloc(sizeof(*sa))) == NULL) err(EXIT_FAILURE, "malloc failed"); if ((sa->name = strdup(".gnu_debuglink")) == NULL) err(EXIT_FAILURE, "strdup failed"); if (stat(ecp->debuglink, &sb) == -1) err(EXIT_FAILURE, "stat failed"); if ((buf = malloc(sb.st_size)) == NULL) err(EXIT_FAILURE, "malloc failed"); if ((fp = fopen(ecp->debuglink, "r")) == NULL) err(EXIT_FAILURE, "can not open %s", ecp->debuglink); if (fread(buf, 1, sb.st_size, fp) == 0 || ferror(fp)) err(EXIT_FAILURE, "fread failed"); fclose(fp); /* Calculate crc checksum. */ crc = calc_crc32(buf, sb.st_size, 0xFFFFFFFF); free(buf); /* Calculate section size and the offset to store crc checksum. */ if ((fnbase = basename(ecp->debuglink)) == NULL) err(EXIT_FAILURE, "basename failed"); crc_off = roundup(strlen(fnbase) + 1, 4); sa->size = crc_off + 4; /* Section content. */ if ((sa->content = calloc(1, sa->size)) == NULL) err(EXIT_FAILURE, "malloc failed"); strncpy(sa->content, fnbase, strlen(fnbase)); if (ecp->oed == ELFDATA2LSB) { sa->content[crc_off] = crc & 0xFF; sa->content[crc_off + 1] = (crc >> 8) & 0xFF; sa->content[crc_off + 2] = (crc >> 16) & 0xFF; sa->content[crc_off + 3] = crc >> 24; } else { sa->content[crc_off] = crc >> 24; sa->content[crc_off + 1] = (crc >> 16) & 0xFF; sa->content[crc_off + 2] = (crc >> 8) & 0xFF; sa->content[crc_off + 3] = crc & 0xFF; } STAILQ_INSERT_TAIL(&ecp->v_sadd, sa, sadd_list); ecp->flags |= SEC_ADD; } static void insert_to_strtab(struct section *t, const char *s) { const char *r; char *b, *c; size_t len, slen; int append; if (t->sz == 0) { t->cap = 512; if ((t->buf = malloc(t->cap)) == NULL) err(EXIT_FAILURE, "malloc failed"); } slen = strlen(s); append = 0; b = t->buf; for (c = b; c < b + t->sz;) { len = strlen(c); if (!append && len >= slen) { r = c + (len - slen); if (strcmp(r, s) == 0) return; } else if (len < slen && len != 0) { r = s + (slen - len); if (strcmp(c, r) == 0) { t->sz -= len + 1; memmove(c, c + len + 1, t->sz - (c - b)); append = 1; continue; } } c += len + 1; } while (t->sz + slen + 1 >= t->cap) { t->cap *= 2; if ((t->buf = realloc(t->buf, t->cap)) == NULL) err(EXIT_FAILURE, "realloc failed"); } b = t->buf; strncpy(&b[t->sz], s, slen); b[t->sz + slen] = '\0'; t->sz += slen + 1; } static int lookup_string(struct section *t, const char *s) { const char *b, *c, *r; size_t len, slen; slen = strlen(s); b = t->buf; for (c = b; c < b + t->sz;) { len = strlen(c); if (len >= slen) { r = c + (len - slen); if (strcmp(r, s) == 0) return (r - b); } c += len + 1; } return (-1); } static uint32_t crctable[256] = { 0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL, 0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L, 0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L, 0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L, 0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL, 0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L, 0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL, 0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L, 0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L, 0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL, 0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L, 0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L, 0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L, 0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL, 0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L, 0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL, 0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL, 0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L, 0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L, 0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L, 0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL, 0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L, 0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL, 0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L, 0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L, 0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL, 0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L, 0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L, 0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L, 0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL, 0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L, 0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL, 0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL, 0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L, 0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L, 0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L, 0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL, 0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L, 0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL, 0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L, 0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L, 0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL, 0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L, 0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L, 0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L, 0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL, 0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L, 0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL, 0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL, 0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L, 0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L, 0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L, 0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL, 0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L, 0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL, 0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L, 0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L, 0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL, 0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L, 0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L, 0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L, 0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL, 0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L, 0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL }; static uint32_t calc_crc32(const char *p, size_t len, uint32_t crc) { uint32_t i; for (i = 0; i < len; i++) { crc = crctable[(crc ^ *p++) & 0xFFL] ^ (crc >> 8); } return (crc ^ 0xFFFFFFFF); } Index: projects/ifnet/contrib/elftoolchain =================================================================== --- projects/ifnet/contrib/elftoolchain (revision 277209) +++ projects/ifnet/contrib/elftoolchain (revision 277210) Property changes on: projects/ifnet/contrib/elftoolchain ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/contrib/elftoolchain:r277180-277209 Index: projects/ifnet/contrib/ntp/ntpd/ntp_io.c =================================================================== --- projects/ifnet/contrib/ntp/ntpd/ntp_io.c (revision 277209) +++ projects/ifnet/contrib/ntp/ntpd/ntp_io.c (revision 277210) @@ -1,4029 +1,4036 @@ /* * ntp_io.c - input/output routines for ntpd. The socket-opening code * was shamelessly stolen from ntpd. */ #ifdef HAVE_CONFIG_H # include #endif #include "ntp_machine.h" #include "ntpd.h" #include "ntp_io.h" #include "iosignal.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" #include "ntp_request.h" #include "ntp.h" #include "ntp_unixtime.h" /* Don't include ISC's version of IPv6 variables and structures */ #define ISC_IPV6_H 1 #include #include #include #ifdef SIM #include "ntpsim.h" #endif #include #include #ifdef HAVE_SYS_PARAM_H # include #endif /* HAVE_SYS_PARAM_H */ #ifdef HAVE_SYS_IOCTL_H # include #endif #ifdef HAVE_SYS_SOCKIO_H /* UXPV: SIOC* #defines (Frank Vance ) */ # include #endif #ifdef HAVE_SYS_UIO_H # include #endif /* * setsockopt does not always have the same arg declaration * across all platforms. If it's not defined we make it empty */ #ifndef SETSOCKOPT_ARG_CAST #define SETSOCKOPT_ARG_CAST #endif /* * Set up some macros to look for IPv6 and IPv6 multicast */ #if defined(ISC_PLATFORM_HAVEIPV6) && !defined(DISABLE_IPV6) #define INCLUDE_IPV6_SUPPORT #if defined(INCLUDE_IPV6_SUPPORT) && defined(IPV6_JOIN_GROUP) && defined(IPV6_LEAVE_GROUP) #define INCLUDE_IPV6_MULTICAST_SUPPORT #endif /* IPV6 Multicast Support */ #endif /* IPv6 Support */ #ifdef INCLUDE_IPV6_SUPPORT #include #include #include #endif /* !INCLUDE_IPV6_SUPPORT */ extern int listen_to_virtual_ips; extern const char *specific_interface; #if defined(SO_TIMESTAMP) && defined(SCM_TIMESTAMP) #if defined(CMSG_FIRSTHDR) #define HAVE_TIMESTAMP #define USE_TIMESTAMP_CMSG #ifndef TIMESTAMP_CTLMSGBUF_SIZE #define TIMESTAMP_CTLMSGBUF_SIZE 1536 /* moderate default */ #endif #else /* fill in for old/other timestamp interfaces */ #endif #endif #if defined(SYS_WINNT) #include #include /* * Define this macro to control the behavior of connection * resets on UDP sockets. See Microsoft KnowledgeBase Article Q263823 * for details. * NOTE: This requires that Windows 2000 systems install Service Pack 2 * or later. */ #ifndef SIO_UDP_CONNRESET #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) #endif /* * Windows C runtime ioctl() can't deal properly with sockets, * map to ioctlsocket for this source file. */ #define ioctl(fd, opt, val) ioctlsocket((fd), (opt), (u_long *)(val)) #endif /* SYS_WINNT */ /* * We do asynchronous input using the SIGIO facility. A number of * recvbuf buffers are preallocated for input. In the signal * handler we poll to see which sockets are ready and read the * packets from them into the recvbuf's along with a time stamp and * an indication of the source host and the interface it was received * through. This allows us to get as accurate receive time stamps * as possible independent of other processing going on. * * We watch the number of recvbufs available to the signal handler * and allocate more when this number drops below the low water * mark. If the signal handler should run out of buffers in the * interim it will drop incoming frames, the idea being that it is * better to drop a packet than to be inaccurate. */ /* * Other statistics of possible interest */ volatile u_long packets_dropped; /* total number of packets dropped on reception */ volatile u_long packets_ignored; /* packets received on wild card interface */ volatile u_long packets_received; /* total number of packets received */ u_long packets_sent; /* total number of packets sent */ u_long packets_notsent; /* total number of packets which couldn't be sent */ volatile u_long handler_calls; /* number of calls to interrupt handler */ volatile u_long handler_pkts; /* number of pkts received by handler */ u_long io_timereset; /* time counters were reset */ /* * Interface stuff */ struct interface *any_interface; /* default ipv4 interface */ struct interface *any6_interface; /* default ipv6 interface */ struct interface *loopback_interface; /* loopback ipv4 interface */ int ninterfaces; /* Total number of interfaces */ volatile int disable_dynamic_updates; /* when set to != 0 dynamic updates won't happen */ #ifdef REFCLOCK /* * Refclock stuff. We keep a chain of structures with data concerning * the guys we are doing I/O for. */ static struct refclockio *refio; #endif /* REFCLOCK */ /* * Define what the possible "soft" errors can be. These are non-fatal returns * of various network related functions, like recv() and so on. * * For some reason, BSDI (and perhaps others) will sometimes return <0 * from recv() but will have errno==0. This is broken, but we have to * work around it here. */ #define SOFT_ERROR(e) ((e) == EAGAIN || \ (e) == EWOULDBLOCK || \ (e) == EINTR || \ (e) == 0) /* * File descriptor masks etc. for call to select * Not needed for I/O Completion Ports */ fd_set activefds; int maxactivefd; /* * bit alternating value to detect verified interfaces during an update cycle */ static u_char sys_interphase = 0; static struct interface *new_interface P((struct interface *)); static void add_interface P((struct interface *)); static int update_interfaces P((u_short, interface_receiver_t, void *)); static void remove_interface P((struct interface *)); static struct interface *create_interface P((u_short, struct interface *)); static int move_fd P((SOCKET)); /* * Multicast functions */ static isc_boolean_t addr_ismulticast P((struct sockaddr_storage *)); /* * Not all platforms support multicast */ #ifdef MCAST static isc_boolean_t socket_multicast_enable P((struct interface *, int, struct sockaddr_storage *)); static isc_boolean_t socket_multicast_disable P((struct interface *, struct sockaddr_storage *)); #endif #ifdef DEBUG static void print_interface P((struct interface *, char *, char *)); #define DPRINT_INTERFACE(_LVL_, _ARGS_) do { if (debug >= (_LVL_)) { print_interface _ARGS_; } } while (0) #else #define DPRINT_INTERFACE(_LVL_, _ARGS_) do {} while (0) #endif typedef struct vsock vsock_t; enum desc_type { FD_TYPE_SOCKET, FD_TYPE_FILE }; struct vsock { SOCKET fd; enum desc_type type; ISC_LINK(vsock_t) link; }; #if !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET) /* * async notification processing (e. g. routing sockets) */ /* * support for receiving data on fd that is not a refclock or a socket * like e. g. routing sockets */ struct asyncio_reader { SOCKET fd; /* fd to be read */ void *data; /* possibly local data */ void (*receiver)(struct asyncio_reader *); /* input handler */ ISC_LINK(struct asyncio_reader) link; /* the list this is being kept in */ }; ISC_LIST(struct asyncio_reader) asyncio_reader_list; static void delete_asyncio_reader P((struct asyncio_reader *)); static struct asyncio_reader *new_asyncio_reader P((void)); static void add_asyncio_reader P((struct asyncio_reader *, enum desc_type)); static void remove_asyncio_reader P((struct asyncio_reader *)); #endif /* !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET) */ static void init_async_notifications P((void)); static int create_sockets P((u_short)); static SOCKET open_socket P((struct sockaddr_storage *, int, int, struct interface *)); static char * fdbits P((int, fd_set *)); static void set_reuseaddr P((int)); static isc_boolean_t socket_broadcast_enable P((struct interface *, SOCKET, struct sockaddr_storage *)); static isc_boolean_t socket_broadcast_disable P((struct interface *, struct sockaddr_storage *)); ISC_LIST(vsock_t) fd_list; typedef struct remaddr remaddr_t; struct remaddr { struct sockaddr_storage addr; struct interface *interface; ISC_LINK(remaddr_t) link; }; ISC_LIST(remaddr_t) remoteaddr_list; ISC_LIST(struct interface) inter_list; static struct interface *wildipv4 = NULL; static struct interface *wildipv6 = NULL; static void add_fd_to_list P((SOCKET, enum desc_type)); static void close_and_delete_fd_from_list P((SOCKET)); static void add_addr_to_list P((struct sockaddr_storage *, struct interface *)); static void delete_addr_from_list P((struct sockaddr_storage *)); static struct interface *find_addr_in_list P((struct sockaddr_storage *)); static struct interface *find_flagged_addr_in_list P((struct sockaddr_storage *, int)); static void create_wildcards P((u_short)); static isc_boolean_t address_okay P((struct interface *)); static void convert_isc_if P((isc_interface_t *, struct interface *, u_short)); static void delete_interface_from_list P((struct interface *)); static struct interface *getinterface P((struct sockaddr_storage *, int)); static struct interface *findlocalinterface P((struct sockaddr_storage *, int)); static struct interface *findlocalcastinterface P((struct sockaddr_storage *, int)); /* * Routines to read the ntp packets */ #if !defined(HAVE_IO_COMPLETION_PORT) static inline int read_network_packet P((SOCKET, struct interface *, l_fp)); static inline int read_refclock_packet P((SOCKET, struct refclockio *, l_fp)); #endif #ifdef SYS_WINNT /* * Windows 2000 systems incorrectly cause UDP sockets using WASRecvFrom * to not work correctly, returning a WSACONNRESET error when a WSASendTo * fails with an "ICMP port unreachable" response and preventing the * socket from using the WSARecvFrom in subsequent operations. * The function below fixes this, but requires that Windows 2000 * Service Pack 2 or later be installed on the system. NT 4.0 * systems are not affected by this and work correctly. * See Microsoft Knowledge Base Article Q263823 for details of this. */ void connection_reset_fix( SOCKET fd, struct sockaddr_storage *addr ) { DWORD dwBytesReturned = 0; BOOL bNewBehavior = FALSE; DWORD status; /* * disable bad behavior using IOCTL: SIO_UDP_CONNRESET * NT 4.0 has no problem */ if (isc_win32os_majorversion() >= 5) { status = WSAIoctl(fd, SIO_UDP_CONNRESET, &bNewBehavior, sizeof(bNewBehavior), NULL, 0, &dwBytesReturned, NULL, NULL); if (SOCKET_ERROR == status) netsyslog(LOG_ERR, "connection_reset_fix() " "failed for address %s: %m", stoa(addr)); } } #endif /* * on Unix systems the stdio library typically * makes use of file descriptors in the lower * integer range. stdio usually will make use * of the file descriptor in the range of * [0..FOPEN_MAX) * in order to keep this range clean for socket * file descriptors we attempt to move them above * FOPEM_MAX. This is not as easy as it sounds as * FOPEN_MAX changes from implementation to implementation * and may exceed to current file decriptor limits. * We are using following strategy: * - keep a current socket fd boundary initialized with * max(0, min(getdtablesize() - FD_CHUNK, FOPEN_MAX)) * - attempt to move the descriptor to the boundary or * above. * - if that fails and boundary > 0 set boundary * to min(0, socket_fd_boundary - FD_CHUNK) * -> retry * if failure and boundary == 0 return old fd * - on success close old fd return new fd * * effects: * - fds will be moved above the socket fd boundary * if at all possible. * - the socket boundary will be reduced until * allocation is possible or 0 is reached - at this * point the algrithm will be disabled */ static int move_fd(SOCKET fd) { #if !defined(SYS_WINNT) && defined(F_DUPFD) #ifndef FD_CHUNK #define FD_CHUNK 10 #endif /* * number of fds we would like to have for * stdio FILE* available. * we can pick a "low" number as our use of * FILE* is limited to log files and temporarily * to data and config files. Except for log files * we don't keep the other FILE* open beyond the * scope of the function that opened it. */ #ifndef FD_PREFERRED_SOCKBOUNDARY #define FD_PREFERRED_SOCKBOUNDARY 48 #endif #ifndef HAVE_GETDTABLESIZE /* * if we have no idea about the max fd value set up things * so we will start at FOPEN_MAX */ #define getdtablesize() (FOPEN_MAX+FD_CHUNK) #endif #ifndef FOPEN_MAX #define FOPEN_MAX 20 /* assume that for the lack of anything better */ #endif static SOCKET socket_boundary = -1; SOCKET newfd; /* * check whether boundary has be set up * already */ if (socket_boundary == -1) { socket_boundary = max(0, min(getdtablesize() - FD_CHUNK, min(FOPEN_MAX, FD_PREFERRED_SOCKBOUNDARY))); #ifdef DEBUG msyslog(LOG_DEBUG, "ntp_io: estimated max descriptors: %d, initial socket boundary: %d", getdtablesize(), socket_boundary); #endif } /* * Leave a space for stdio to work in. potentially moving the * socket_boundary lower until allocation succeeds. */ do { if (fd >= 0 && fd < socket_boundary) { /* inside reserved range: attempt to move fd */ newfd = fcntl(fd, F_DUPFD, socket_boundary); if (newfd != -1) { /* success: drop the old one - return the new one */ (void)close(fd); return (newfd); } } else { /* outside reserved range: no work - return the original one */ return (fd); } socket_boundary = max(0, socket_boundary - FD_CHUNK); #ifdef DEBUG msyslog(LOG_DEBUG, "ntp_io: selecting new socket boundary: %d", socket_boundary); #endif } while (socket_boundary > 0); #endif /* !defined(SYS_WINNT) && defined(F_DUPFD) */ return (fd); } #ifdef DEBUG_TIMING /* * collect timing information for various processing * paths. currently we only pass then on to the file * for later processing. this could also do histogram * based analysis in other to reduce the load (and skew) * dur to the file output */ void collect_timing(struct recvbuf *rb, const char *tag, int count, l_fp *dts) { char buf[2048]; snprintf(buf, sizeof(buf), "%s %d %s %s", (rb != NULL) ? ((rb->dstadr) ? stoa(&rb->recv_srcadr) : "-REFCLOCK-") : "-", count, lfptoa(dts, 9), tag); record_timing_stats(buf); } #endif /* * About dynamic interfaces, sockets, reception and more... * * the code solves following tasks: * * - keep a current list of active interfaces in order * to bind to to the interface address on NTP_PORT so that * all wild and specific bindings for NTP_PORT are taken by ntpd * to avoid other daemons messing with the time or sockets. * - all interfaces keep a list of peers that are referencing * the interface in order to quickly re-assign the peers to * new interface in case an interface is deleted (=> gone from system or * down) * - have a preconfigured socket ready with the right local address * for transmission and reception * - have an address list for all destination addresses used within ntpd * to find the "right" preconfigured socket. * - facilitate updating the internal interface list with respect to * the current kernel state * * special issues: * * - mapping of multicast addresses to the interface affected is not always * one to one - especially on hosts with multiple interfaces * the code here currently allocates a separate interface entry for those * multicast addresses * iff it is able to bind to a *new* socket with the multicast address (flags |= MCASTIF) * in case of failure the multicast address is bound to an existing interface. * - on some systems it is perfectly legal to assign the same address to * multiple interfaces. Therefore this code does not keep a list of interfaces * but a list of interfaces that represent a unique address as determined by the kernel * by the procedure in findlocalinterface. Thus it is perfectly legal to see only * one representative of a group of real interfaces if they share the same address. * * Frank Kardel 20050910 */ /* * init_io - initialize I/O data structures and call socket creation routine */ void init_io(void) { #ifdef SYS_WINNT init_io_completion_port(); if (!Win32InitSockets()) { netsyslog(LOG_ERR, "No useable winsock.dll: %m"); exit(1); } init_transmitbuff(); #endif /* SYS_WINNT */ /* * Init buffer free list and stat counters */ init_recvbuff(RECV_INIT); packets_dropped = packets_received = 0; packets_ignored = 0; packets_sent = packets_notsent = 0; handler_calls = handler_pkts = 0; io_timereset = 0; loopback_interface = NULL; any_interface = NULL; any6_interface = NULL; #ifdef REFCLOCK refio = NULL; #endif #if defined(HAVE_SIGNALED_IO) (void) set_signal(); #endif ISC_LIST_INIT(fd_list); #if !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET) ISC_LIST_INIT(asyncio_reader_list); #endif ISC_LIST_INIT(remoteaddr_list); ISC_LIST_INIT(inter_list); /* * Create the sockets */ BLOCKIO(); (void) create_sockets(htons(NTP_PORT)); UNBLOCKIO(); init_async_notifications(); DPRINTF(3, ("init_io: maxactivefd %d\n", maxactivefd)); } #ifdef DEBUG /* * function to dump the contents of the interface structure * for debugging use only. */ void interface_dump(struct interface *itf) { u_char* cp; int i; /* Limit the size of the sockaddr_storage hex dump */ int maxsize = min(32, sizeof(struct sockaddr_storage)); printf("Dumping interface: %p\n", itf); printf("fd = %d\n", itf->fd); printf("bfd = %d\n", itf->bfd); printf("sin = %s,\n", stoa(&(itf->sin))); cp = (u_char*) &(itf->sin); for(i = 0; i < maxsize; i++) { printf("%02x", *cp++); if((i+1)%4 == 0) printf(" "); } printf("\n"); printf("bcast = %s,\n", stoa(&(itf->bcast))); cp = (u_char*) &(itf->bcast); for(i = 0; i < maxsize; i++) { printf("%02x", *cp++); if((i+1)%4 == 0) printf(" "); } printf("\n"); printf("mask = %s,\n", stoa(&(itf->mask))); cp = (u_char*) &(itf->mask); for(i = 0; i < maxsize; i++) { printf("%02x", *cp++); if((i+1)%4 == 0) printf(" "); } printf("\n"); printf("name = %s\n", itf->name); printf("flags = 0x%08x\n", itf->flags); printf("last_ttl = %d\n", itf->last_ttl); printf("addr_refid = %08x\n", itf->addr_refid); printf("num_mcast = %d\n", itf->num_mcast); printf("received = %ld\n", itf->received); printf("sent = %ld\n", itf->sent); printf("notsent = %ld\n", itf->notsent); printf("ifindex = %u\n", itf->ifindex); printf("scopeid = %u\n", itf->scopeid); printf("peercnt = %u\n", itf->peercnt); printf("phase = %u\n", itf->phase); } /* * print_interface - helper to output debug information */ static void print_interface(struct interface *iface, char *pfx, char *sfx) { printf("%sinterface #%d: fd=%d, bfd=%d, name=%s, flags=0x%x, scope=%d, ifindex=%d", pfx, iface->ifnum, iface->fd, iface->bfd, iface->name, iface->flags, iface->scopeid, iface->ifindex); /* Leave these as three printf calls. */ printf(", sin=%s", stoa((&iface->sin))); if (iface->flags & INT_BROADCAST) printf(", bcast=%s,", stoa((&iface->bcast))); if (iface->family == AF_INET) printf(", mask=%s", stoa((&iface->mask))); printf(", %s:%s", iface->ignore_packets == ISC_FALSE ? "Enabled" : "Disabled", sfx); if (debug > 4) /* in-depth debugging only */ interface_dump(iface); } #endif #if !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET) /* * create an asyncio_reader structure */ static struct asyncio_reader * new_asyncio_reader() { struct asyncio_reader *reader; reader = (struct asyncio_reader *)emalloc(sizeof(struct asyncio_reader)); memset((char *)reader, 0, sizeof(*reader)); ISC_LINK_INIT(reader, link); reader->fd = INVALID_SOCKET; return reader; } /* * delete a reader */ static void delete_asyncio_reader(struct asyncio_reader *reader) { free(reader); } /* * add asynchio_reader */ static void add_asyncio_reader(struct asyncio_reader *reader, enum desc_type type) { ISC_LIST_APPEND(asyncio_reader_list, reader, link); add_fd_to_list(reader->fd, type); } /* * remove asynchio_reader */ static void remove_asyncio_reader(struct asyncio_reader *reader) { ISC_LIST_UNLINK_TYPE(asyncio_reader_list, reader, link, struct asyncio_reader); if (reader->fd != INVALID_SOCKET) close_and_delete_fd_from_list(reader->fd); reader->fd = INVALID_SOCKET; } #endif /* !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET) */ /* * interface list enumerator - visitor pattern */ void interface_enumerate(interface_receiver_t receiver, void *data) { interface_info_t ifi; struct interface *interf; ifi.action = IFS_EXISTS; for (interf = ISC_LIST_HEAD(inter_list); interf != NULL; interf = ISC_LIST_NEXT(interf, link)) { ifi.interface = interf; receiver(data, &ifi); } } /* * do standard initialization of interface structure */ static void init_interface(struct interface *interface) { memset((char *)interface, 0, sizeof(struct interface)); ISC_LINK_INIT(interface, link); ISC_LIST_INIT(interface->peers); interface->fd = INVALID_SOCKET; interface->bfd = INVALID_SOCKET; interface->num_mcast = 0; interface->received = 0; interface->sent = 0; interface->notsent = 0; interface->peercnt = 0; interface->phase = sys_interphase; } /* * create new interface structure initialize from * template structure or via standard initialization * function */ static struct interface * new_interface(struct interface *interface) { static u_int sys_ifnum = 0; struct interface *iface = (struct interface *)emalloc(sizeof(struct interface)); if (interface != NULL) { memcpy((char*)iface, (char*)interface, sizeof(*interface)); } else { init_interface(iface); } iface->ifnum = sys_ifnum++; /* count every new instance of an interface in the system */ iface->starttime = current_time; return iface; } /* * return interface storage into free memory pool */ static void delete_interface(struct interface *interface) { free(interface); } /* * link interface into list of known interfaces */ static void add_interface(struct interface *interface) { static struct interface *listhead = NULL; /* * For ntpd, the first few interfaces (wildcard, localhost) * will never be removed. This means inter_list.head is * unchanging once initialized. Take advantage of that to * watch for changes and catch corruption earlier. This * helped track down corruption caused by using FD_SET with * a descriptor numerically larger than FD_SETSIZE. */ if (NULL == listhead) listhead = inter_list.head; if (listhead != inter_list.head) { msyslog(LOG_ERR, "add_interface inter_list.head corrupted: was %p now %p", listhead, inter_list.head); exit(1); } /* * Calculate the address hash */ interface->addr_refid = addr2refid(&interface->sin); ISC_LIST_APPEND(inter_list, interface, link); ninterfaces++; } /* * remove interface from known interface list and clean up * associated resources */ static void remove_interface(struct interface *interface) { struct sockaddr_storage resmask; ISC_LIST_UNLINK_TYPE(inter_list, interface, link, struct interface); delete_interface_from_list(interface); if (interface->fd != INVALID_SOCKET) { msyslog(LOG_INFO, "Deleting interface #%d %s, %s#%d, interface stats: received=%ld, sent=%ld, dropped=%ld, active_time=%ld secs", interface->ifnum, interface->name, stoa((&interface->sin)), NTP_PORT, /* XXX should extract port from sin structure */ interface->received, interface->sent, interface->notsent, current_time - interface->starttime); close_and_delete_fd_from_list(interface->fd); } if (interface->bfd != INVALID_SOCKET) { msyslog(LOG_INFO, "Deleting interface #%d %s, broadcast address %s#%d", interface->ifnum, interface->name, stoa((&interface->bcast)), (u_short) NTP_PORT); /* XXX extract port from sin structure */ close_and_delete_fd_from_list(interface->bfd); } ninterfaces--; ntp_monclearinterface(interface); /* remove restrict interface entry */ /* * Blacklist bound interface address */ SET_HOSTMASK(&resmask, interface->sin.ss_family); hack_restrict(RESTRICT_REMOVEIF, &interface->sin, &resmask, RESM_NTPONLY|RESM_INTERFACE, RES_IGNORE); } static void list_if_listening(struct interface *interface, u_short port) { msyslog(LOG_INFO, "Listening on interface #%d %s, %s#%d %s", interface->ifnum, interface->name, stoa((&interface->sin)), ntohs( (u_short) port), (interface->ignore_packets == ISC_FALSE) ? "Enabled": "Disabled"); } static void create_wildcards(u_short port) { isc_boolean_t okipv4 = ISC_TRUE; /* * create pseudo-interface with wildcard IPv4 address */ #ifdef IPV6_V6ONLY if(isc_net_probeipv4() != ISC_R_SUCCESS) okipv4 = ISC_FALSE; #endif if(okipv4 == ISC_TRUE) { struct interface *interface = new_interface(NULL); interface->family = AF_INET; interface->sin.ss_family = AF_INET; ((struct sockaddr_in*)&interface->sin)->sin_addr.s_addr = htonl(INADDR_ANY); ((struct sockaddr_in*)&interface->sin)->sin_port = port; (void) strncpy(interface->name, "wildcard", sizeof(interface->name)); interface->mask.ss_family = AF_INET; ((struct sockaddr_in*)&interface->mask)->sin_addr.s_addr = htonl(~(u_int32)0); interface->flags = INT_BROADCAST | INT_UP | INT_WILDCARD; interface->ignore_packets = ISC_TRUE; #if defined(MCAST) /* * enable possible multicast reception on the broadcast socket */ interface->bcast.ss_family = AF_INET; ((struct sockaddr_in*)&interface->bcast)->sin_port = port; ((struct sockaddr_in*)&interface->bcast)->sin_addr.s_addr = htonl(INADDR_ANY); #endif /* MCAST */ interface->fd = open_socket(&interface->sin, interface->flags, 1, interface); if (interface->fd != INVALID_SOCKET) { wildipv4 = interface; any_interface = interface; add_addr_to_list(&interface->sin, interface); add_interface(interface); list_if_listening(interface, port); } else { msyslog(LOG_ERR, "unable to bind to wildcard socket address %s - another process may be running - EXITING", stoa((&interface->sin))); exit(1); } } #ifdef INCLUDE_IPV6_SUPPORT /* * create pseudo-interface with wildcard IPv6 address */ if (isc_net_probeipv6() == ISC_R_SUCCESS) { struct interface *interface = new_interface(NULL); interface->family = AF_INET6; interface->sin.ss_family = AF_INET6; ((struct sockaddr_in6*)&interface->sin)->sin6_addr = in6addr_any; ((struct sockaddr_in6*)&interface->sin)->sin6_port = port; # ifdef ISC_PLATFORM_HAVESCOPEID ((struct sockaddr_in6*)&interface->sin)->sin6_scope_id = 0; # endif (void) strncpy(interface->name, "wildcard", sizeof(interface->name)); interface->mask.ss_family = AF_INET6; memset(&((struct sockaddr_in6*)&interface->mask)->sin6_addr.s6_addr, 0xff, sizeof(struct in6_addr)); interface->flags = INT_UP | INT_WILDCARD; interface->ignore_packets = ISC_TRUE; interface->fd = open_socket(&interface->sin, interface->flags, 1, interface); if (interface->fd != INVALID_SOCKET) { wildipv6 = interface; any6_interface = interface; add_addr_to_list(&interface->sin, interface); add_interface(interface); list_if_listening(interface, port); } else { msyslog(LOG_ERR, "unable to bind to wildcard socket address %s - another process may be running - EXITING", stoa((&interface->sin))); exit(1); } } #endif } static isc_boolean_t address_okay(struct interface *iface) { DPRINTF(4, ("address_okay: listen Virtual: %d, IF name: %s\n", listen_to_virtual_ips, iface->name)); /* * Always allow the loopback */ if((iface->flags & INT_LOOPBACK) != 0) { DPRINTF(4, ("address_okay: loopback - OK\n")); return (ISC_TRUE); } /* * Check if the interface is specified */ if (specific_interface != NULL) { if (strcasecmp(iface->name, specific_interface) == 0) { DPRINTF(4, ("address_okay: specific interface name matched - OK\n")); return (ISC_TRUE); } else { DPRINTF(4, ("address_okay: specific interface name NOT matched - FAIL\n")); return (ISC_FALSE); } } else { if (listen_to_virtual_ips == 0 && (strchr(iface->name, (int)':') != NULL)) { DPRINTF(4, ("address_okay: virtual ip/alias - FAIL\n")); return (ISC_FALSE); } } DPRINTF(4, ("address_okay: OK\n")); return (ISC_TRUE); } static void convert_isc_if(isc_interface_t *isc_if, struct interface *itf, u_short port) { itf->scopeid = 0; itf->family = (short) isc_if->af; strcpy(itf->name, isc_if->name); if(isc_if->af == AF_INET) { itf->sin.ss_family = (u_short) isc_if->af; memcpy(&(((struct sockaddr_in*)&itf->sin)->sin_addr), &(isc_if->address.type.in), sizeof(struct in_addr)); ((struct sockaddr_in*)&itf->sin)->sin_port = port; if((isc_if->flags & INTERFACE_F_BROADCAST) != 0) { itf->flags |= INT_BROADCAST; itf->bcast.ss_family = itf->sin.ss_family; memcpy(&(((struct sockaddr_in*)&itf->bcast)->sin_addr), &(isc_if->broadcast.type.in), sizeof(struct in_addr)); ((struct sockaddr_in*)&itf->bcast)->sin_port = port; } itf->mask.ss_family = itf->sin.ss_family; memcpy(&(((struct sockaddr_in*)&itf->mask)->sin_addr), &(isc_if->netmask.type.in), sizeof(struct in_addr)); ((struct sockaddr_in*)&itf->mask)->sin_port = port; } #ifdef INCLUDE_IPV6_SUPPORT else if (isc_if->af == AF_INET6) { itf->sin.ss_family = (u_short) isc_if->af; memcpy(&(((struct sockaddr_in6 *)&itf->sin)->sin6_addr), &(isc_if->address.type.in6), sizeof(((struct sockaddr_in6 *)&itf->sin)->sin6_addr)); ((struct sockaddr_in6 *)&itf->sin)->sin6_port = port; #ifdef ISC_PLATFORM_HAVESCOPEID ((struct sockaddr_in6 *)&itf->sin)->sin6_scope_id = isc_netaddr_getzone(&isc_if->address); itf->scopeid = isc_netaddr_getzone(&isc_if->address); #endif itf->mask.ss_family = itf->sin.ss_family; memcpy(&(((struct sockaddr_in6 *)&itf->mask)->sin6_addr), &(isc_if->netmask.type.in6), sizeof(struct in6_addr)); ((struct sockaddr_in6 *)&itf->mask)->sin6_port = port; /* Copy the interface index */ itf->ifindex = isc_if->ifindex; } #endif /* INCLUDE_IPV6_SUPPORT */ /* Process the rest of the flags */ if((isc_if->flags & INTERFACE_F_UP) != 0) itf->flags |= INT_UP; if((isc_if->flags & INTERFACE_F_LOOPBACK) != 0) itf->flags |= INT_LOOPBACK; if((isc_if->flags & INTERFACE_F_POINTTOPOINT) != 0) itf->flags |= INT_PPP; if((isc_if->flags & INTERFACE_F_MULTICAST) != 0) itf->flags |= INT_MULTICAST; } /* * refresh_interface * * some OSes have been observed to keep * cached routes even when more specific routes * become available. * this can be mitigated by re-binding * the socket. */ static int refresh_interface(struct interface * interface) { #ifdef OS_MISSES_SPECIFIC_ROUTE_UPDATES if (interface->fd != INVALID_SOCKET) { close_and_delete_fd_from_list(interface->fd); interface->fd = open_socket(&interface->sin, interface->flags, 0, interface); /* * reset TTL indication so TTL is is set again * next time around */ interface->last_ttl = 0; return interface->fd != INVALID_SOCKET; } else { return 0; /* invalid sockets are not refreshable */ } #else /* !OS_MISSES_SPECIFIC_ROUTE_UPDATES */ return interface->fd != INVALID_SOCKET; #endif /* !OS_MISSES_SPECIFIC_ROUTE_UPDATES */ } /* * interface_update - externally callable update function */ void interface_update(interface_receiver_t receiver, void *data) { if (!disable_dynamic_updates) { int new_interface_found; BLOCKIO(); new_interface_found = update_interfaces(htons(NTP_PORT), receiver, data); UNBLOCKIO(); if (new_interface_found) { #ifdef DEBUG msyslog(LOG_DEBUG, "new interface(s) found: waking up resolver"); #endif #ifdef SYS_WINNT /* wake up the resolver thread */ if (ResolverEventHandle != NULL) SetEvent(ResolverEventHandle); #else /* write any single byte to the pipe to wake up the resolver process */ write( resolver_pipe_fd[1], &new_interface_found, 1 ); #endif } } } /* * find out if a given interface structure contains * a wildcard address */ static int is_wildcard_addr(struct sockaddr_storage *sas) { if (sas->ss_family == AF_INET && ((struct sockaddr_in*)sas)->sin_addr.s_addr == htonl(INADDR_ANY)) return 1; #ifdef INCLUDE_IPV6_SUPPORT if (sas->ss_family == AF_INET6 && memcmp(&((struct sockaddr_in6*)sas)->sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0) return 1; #endif return 0; } #ifdef OS_NEEDS_REUSEADDR_FOR_IFADDRBIND /* * enable/disable re-use of wildcard address socket */ static void set_wildcard_reuse(int family, int on) { int onvalue = 1; int offvalue = 0; int *onoff; SOCKET fd = INVALID_SOCKET; onoff = on ? &onvalue : &offvalue; switch (family) { case AF_INET: if (any_interface) { fd = any_interface->fd; } break; #ifdef INCLUDE_IPV6_SUPPORT case AF_INET6: if (any6_interface) { fd = any6_interface->fd; } break; #endif /* !INCLUDE_IPV6_SUPPORT */ } if (fd != INVALID_SOCKET) { if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)onoff, sizeof(*onoff))) { netsyslog(LOG_ERR, "set_wildcard_reuse: setsockopt(SO_REUSEADDR, %s) failed: %m", *onoff ? "on" : "off"); } DPRINTF(4, ("set SO_REUSEADDR to %s on %s\n", *onoff ? "ON" : "OFF", stoa((family == AF_INET) ? &any_interface->sin : &any6_interface->sin))); } } #endif /* OS_NEEDS_REUSEADDR_FOR_IFADDRBIND */ #ifdef INCLUDE_IPV6_SUPPORT static isc_boolean_t -is_anycast(struct sockaddr *sa, char *name) +is_not_bindable(struct sockaddr *sa, char *name) { -#if defined(SIOCGIFAFLAG_IN6) && defined(IN6_IFF_ANYCAST) +#if defined(SIOCGIFAFLAG_IN6) && \ + (defined(IN6_IFF_ANYCAST) || defined(IN6_IFF_NOTREADY)) struct in6_ifreq ifr6; int fd; - u_int32_t flags6; + u_int32_t flags6, exclude = 0; if (sa->sa_family != AF_INET6) return ISC_FALSE; if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) return ISC_FALSE; memset(&ifr6, 0, sizeof(ifr6)); memcpy(&ifr6.ifr_addr, (struct sockaddr_in6 *)sa, sizeof(struct sockaddr_in6)); strlcpy(ifr6.ifr_name, name, IF_NAMESIZE); if (ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) < 0) { close(fd); return ISC_FALSE; } close(fd); flags6 = ifr6.ifr_ifru.ifru_flags6; - if ((flags6 & IN6_IFF_ANYCAST) != 0) +#if defined(IN6_IFF_ANYCAST) + exclude |= IN6_IFF_ANYCAST; +#endif /* !IN6_IFF_ANYCAST */ +#if defined(IN6_IFF_NOTREADY) + exclude |= IN6_IFF_NOTREADY; +#endif /* !IN6_IFF_NOTREADY */ + if ((flags6 & exclude) != 0) return ISC_TRUE; -#endif /* !SIOCGIFAFLAG_IN6 || !IN6_IFF_ANYCAST */ +#endif /* !SIOCGIFAFLAG_IN6 || !(IN6_IFF_ANYCAST && IN6_IFF_NOTREADY) */ return ISC_FALSE; } #endif /* !INCLUDE_IPV6_SUPPORT */ /* * update_interface strategy * * toggle configuration phase * * Phase 1: * forall currently existing interfaces * if address is known: * drop socket - rebind again * * if address is NOT known: * attempt to create a new interface entry * * Phase 2: * forall currently known non MCAST and WILDCARD interfaces * if interface does not match configuration phase (not seen in phase 1): * remove interface from known interface list * forall peers associated with this interface * disconnect peer from this interface * * Phase 3: * attempt to re-assign interfaces to peers * */ static int update_interfaces( u_short port, interface_receiver_t receiver, void *data ) { interface_info_t ifi; isc_mem_t *mctx = NULL; isc_interfaceiter_t *iter = NULL; isc_boolean_t scan_ipv4 = ISC_FALSE; isc_boolean_t scan_ipv6 = ISC_FALSE; isc_result_t result; int new_interface_found = 0; DPRINTF(3, ("update_interfaces(%d)\n", ntohs( (u_short) port))); #ifdef INCLUDE_IPV6_SUPPORT if (isc_net_probeipv6() == ISC_R_SUCCESS) scan_ipv6 = ISC_TRUE; #if defined(DEBUG) else if (debug) netsyslog(LOG_ERR, "no IPv6 interfaces found"); #endif #endif if (isc_net_probeipv6() == ISC_R_SUCCESS) scan_ipv6 = ISC_TRUE; #if defined(ISC_PLATFORM_HAVEIPV6) && defined(DEBUG) else if (debug) netsyslog(LOG_ERR, "no IPv6 interfaces found"); #endif if (isc_net_probeipv4() == ISC_R_SUCCESS) scan_ipv4 = ISC_TRUE; #ifdef DEBUG else if(debug) netsyslog(LOG_ERR, "no IPv4 interfaces found"); #endif /* * phase one - scan interfaces * - create those that are not found * - update those that are found */ result = isc_interfaceiter_create(mctx, &iter); if (result != ISC_R_SUCCESS) return 0; sys_interphase ^= 0x1; /* toggle system phase for finding untouched (to be deleted) interfaces */ for (result = isc_interfaceiter_first(iter); result == ISC_R_SUCCESS; result = isc_interfaceiter_next(iter)) { isc_interface_t isc_if; unsigned int family; struct interface interface; struct interface *iface; result = isc_interfaceiter_current(iter, &isc_if); if (result != ISC_R_SUCCESS) break; /* See if we have a valid family to use */ family = isc_if.address.family; if (family != AF_INET && family != AF_INET6) continue; if (scan_ipv4 == ISC_FALSE && family == AF_INET) continue; if (scan_ipv6 == ISC_FALSE && family == AF_INET6) continue; /* * create prototype */ init_interface(&interface); convert_isc_if(&isc_if, &interface, port); /* * Check to see if we are going to use the interface * If we don't use it we mark it to drop any packet * received but we still must create the socket and * bind to it. This prevents other apps binding to it * and potentially causing problems with more than one * process fiddling with the clock */ if (address_okay(&interface) == ISC_TRUE) { interface.ignore_packets = ISC_FALSE; } else { interface.ignore_packets = ISC_TRUE; } DPRINT_INTERFACE(4, (&interface, "examining ", "\n")); if (!(interface.flags & INT_UP)) { /* interfaces must be UP to be usable */ DPRINTF(4, ("skipping interface %s (%s) - DOWN\n", interface.name, stoa(&interface.sin))); continue; } /* * skip any interfaces UP and bound to a wildcard * address - some dhcp clients produce that in the * wild */ if (is_wildcard_addr(&interface.sin)) continue; #ifdef INCLUDE_IPV6_SUPPORT - if (is_anycast((struct sockaddr *)&interface.sin, isc_if.name)) + if (is_not_bindable((struct sockaddr *)&interface.sin, isc_if.name)) continue; #endif /* !INCLUDE_IPV6_SUPPORT */ /* * map to local *address* in order * to map all duplicate interfaces to an interface structure * with the appropriate socket (our name space is * (ip-address) - NOT (interface name, ip-address)) */ iface = getinterface(&interface.sin, INT_WILDCARD); if (iface && refresh_interface(iface)) { /* * found existing and up to date interface - mark present */ iface->phase = sys_interphase; DPRINT_INTERFACE(4, (iface, "updating ", " present\n")); ifi.action = IFS_EXISTS; ifi.interface = iface; if (receiver) receiver(data, &ifi); } else { /* * this is new or refreshing failed - add to our interface list * if refreshing failed we will delete the interface structure in * phase 2 as the interface was not marked current. We can bind to * the address as the refresh code already closed the offending socket */ iface = create_interface(port, &interface); if (iface) { ifi.action = IFS_CREATED; ifi.interface = iface; if (receiver) receiver(data, &ifi); new_interface_found = 1; DPRINT_INTERFACE(3, (iface, "updating ", " new - created\n")); } else { DPRINT_INTERFACE(3, (&interface, "updating ", " new - creation FAILED")); msyslog(LOG_INFO, "failed to initialize interface for address %s", stoa(&interface.sin)); continue; } } } isc_interfaceiter_destroy(&iter); /* * phase 2 - delete gone interfaces - reassigning peers to other interfaces */ { struct interface *interf = ISC_LIST_HEAD(inter_list); while (interf != NULL) { struct interface *next = ISC_LIST_NEXT(interf, link); if (!(interf->flags & (INT_WILDCARD|INT_MCASTIF))) { /* * if phase does not match sys_phase this interface was not * enumerated during interface scan - so it is gone and * will be deleted here unless it is solely an MCAST/WILDCARD interface */ if (interf->phase != sys_interphase) { struct peer *peer; DPRINT_INTERFACE(3, (interf, "updating ", "GONE - deleting\n")); remove_interface(interf); ifi.action = IFS_DELETED; ifi.interface = interf; if (receiver) receiver(data, &ifi); peer = ISC_LIST_HEAD(interf->peers); /* * disconnect peer from deleted interface */ while (peer != NULL) { struct peer *npeer = ISC_LIST_NEXT(peer, ilink); /* * this one just lost it's interface */ set_peerdstadr(peer, NULL); peer = npeer; } /* * update globals in case we lose * a loopback interface */ if (interf == loopback_interface) loopback_interface = NULL; delete_interface(interf); } } interf = next; } } /* * phase 3 - re-configure as the world has changed if necessary */ refresh_all_peerinterfaces(); return new_interface_found; } /* * create_sockets - create a socket for each interface plus a default * socket for when we don't know where to send */ static int create_sockets( u_short port ) { #ifndef HAVE_IO_COMPLETION_PORT /* * I/O Completion Ports don't care about the select and FD_SET */ maxactivefd = 0; FD_ZERO(&activefds); #endif DPRINTF(2, ("create_sockets(%d)\n", ntohs( (u_short) port))); create_wildcards(port); update_interfaces(port, NULL, NULL); /* * Now that we have opened all the sockets, turn off the reuse * flag for security. */ set_reuseaddr(0); DPRINTF(2, ("create_sockets: Total interfaces = %d\n", ninterfaces)); return ninterfaces; } /* * create_interface - create a new interface for a given prototype * binding the socket. */ static struct interface * create_interface( u_short port, struct interface *iface ) { struct sockaddr_storage resmask; struct interface *interface; DPRINTF(2, ("create_interface(%s#%d)\n", stoa(&iface->sin), ntohs( (u_short) port))); /* build an interface */ interface = new_interface(iface); /* * create socket */ interface->fd = open_socket(&interface->sin, interface->flags, 0, interface); if (interface->fd != INVALID_SOCKET) list_if_listening(interface, port); if ((interface->flags & INT_BROADCAST) && interface->bfd != INVALID_SOCKET) msyslog(LOG_INFO, "Listening on broadcast address %s#%d", stoa((&interface->bcast)), ntohs( (u_short) port)); if (interface->fd == INVALID_SOCKET && interface->bfd == INVALID_SOCKET) { msyslog(LOG_ERR, "unable to create socket on %s (%d) for %s#%d", interface->name, interface->ifnum, stoa((&interface->sin)), ntohs( (u_short) port)); delete_interface(interface); return NULL; } /* * Blacklist bound interface address */ SET_HOSTMASK(&resmask, interface->sin.ss_family); hack_restrict(RESTRICT_FLAGS, &interface->sin, &resmask, RESM_NTPONLY|RESM_INTERFACE, RES_IGNORE); /* * set globals with the first found * loopback interface of the appropriate class */ if ((loopback_interface == NULL) && (interface->family == AF_INET) && ((interface->flags & INT_LOOPBACK) != 0)) { loopback_interface = interface; } /* * put into our interface list */ add_addr_to_list(&interface->sin, interface); add_interface(interface); DPRINT_INTERFACE(2, (interface, "created ", "\n")); return interface; } #ifdef SO_EXCLUSIVEADDRUSE static void set_excladdruse(int fd) { int one = 1; int failed; failed = setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&one, sizeof(one)); if (failed) netsyslog(LOG_ERR, "setsockopt(%d, SO_EXCLUSIVEADDRUSE, on): %m", fd); } #endif /* SO_EXCLUSIVEADDRUSE */ /* * set_reuseaddr() - set/clear REUSEADDR on all sockets * NB possible hole - should we be doing this on broadcast * fd's also? */ static void set_reuseaddr(int flag) { struct interface *interf; #ifndef SO_EXCLUSIVEADDRUSE for (interf = ISC_LIST_HEAD(inter_list); interf != NULL; interf = ISC_LIST_NEXT(interf, link)) { if (interf->flags & INT_WILDCARD) continue; /* * if interf->fd is INVALID_SOCKET, we might have a adapter * configured but not present */ DPRINTF(4, ("setting SO_REUSEADDR on %.16s@%s to %s\n", interf->name, stoa(&interf->sin), flag ? "on" : "off")); if (interf->fd != INVALID_SOCKET) { if (setsockopt(interf->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag))) { netsyslog(LOG_ERR, "set_reuseaddr: setsockopt(SO_REUSEADDR, %s) failed: %m", flag ? "on" : "off"); } } } #endif /* ! SO_EXCLUSIVEADDRUSE */ } /* * This is just a wrapper around an internal function so we can * make other changes as necessary later on */ void enable_broadcast(struct interface *iface, struct sockaddr_storage *baddr) { #ifdef SO_BROADCAST socket_broadcast_enable(iface, iface->fd, baddr); #endif } #ifdef OPEN_BCAST_SOCKET /* * Enable a broadcast address to a given socket * The socket is in the inter_list all we need to do is enable * broadcasting. It is not this function's job to select the socket */ static isc_boolean_t socket_broadcast_enable(struct interface *iface, SOCKET fd, struct sockaddr_storage *maddr) { #ifdef SO_BROADCAST int on = 1; if (maddr->ss_family == AF_INET) { /* if this interface can support broadcast, set SO_BROADCAST */ if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof(on))) { netsyslog(LOG_ERR, "setsockopt(SO_BROADCAST) enable failure on address %s: %m", stoa(maddr)); } #ifdef DEBUG else if (debug > 1) { printf("Broadcast enabled on socket %d for address %s\n", fd, stoa(maddr)); } #endif } iface->flags |= INT_BCASTOPEN; return ISC_TRUE; #else return ISC_FALSE; #endif /* SO_BROADCAST */ } /* * Remove a broadcast address from a given socket * The socket is in the inter_list all we need to do is disable * broadcasting. It is not this function's job to select the socket */ static isc_boolean_t socket_broadcast_disable(struct interface *iface, struct sockaddr_storage *maddr) { #ifdef SO_BROADCAST int off = 0; /* This seems to be OK as an int */ if (maddr->ss_family == AF_INET) { if (setsockopt(iface->fd, SOL_SOCKET, SO_BROADCAST, (char *)&off, sizeof(off))) { netsyslog(LOG_ERR, "setsockopt(SO_BROADCAST) disable failure on address %s: %m", stoa(maddr)); } } iface->flags &= ~INT_BCASTOPEN; return ISC_TRUE; #else return ISC_FALSE; #endif /* SO_BROADCAST */ } #endif /* OPEN_BCAST_SOCKET */ /* * Check to see if the address is a multicast address */ static isc_boolean_t addr_ismulticast(struct sockaddr_storage *maddr) { switch (maddr->ss_family) { case AF_INET : if (!IN_CLASSD(ntohl(((struct sockaddr_in*)maddr)->sin_addr.s_addr))) { DPRINTF(4, ("multicast address %s not class D\n", stoa(maddr))); return (ISC_FALSE); } else { return (ISC_TRUE); } case AF_INET6 : #ifdef INCLUDE_IPV6_MULTICAST_SUPPORT if (!IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)maddr)->sin6_addr)) { DPRINTF(4, ("address %s not IPv6 multicast address\n", stoa(maddr))); return (ISC_FALSE); } else { return (ISC_TRUE); } /* * If we don't have IPV6 support any IPV6 address is not multicast */ #else return (ISC_FALSE); #endif /* * Never valid */ default: return (ISC_FALSE); } } /* * Multicast servers need to set the appropriate Multicast interface * socket option in order for it to know which interface to use for * send the multicast packet. */ void enable_multicast_if(struct interface *iface, struct sockaddr_storage *maddr) { #ifdef MCAST #ifdef IP_MULTICAST_LOOP /*u_char*/ TYPEOF_IP_MULTICAST_LOOP off = 0; #endif #ifdef IPV6_MULTICAST_LOOP u_int off6 = 0; /* RFC 3493, 5.2. defines type unsigned int */ #endif switch (maddr->ss_family) { case AF_INET: if (setsockopt(iface->fd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&(((struct sockaddr_in*)&iface->sin)->sin_addr.s_addr), sizeof(struct in_addr)) == -1) { netsyslog(LOG_ERR, "setsockopt IP_MULTICAST_IF failure: %m on socket %d, addr %s for multicast address %s", iface->fd, stoa(&iface->sin), stoa(maddr)); return; } #ifdef IP_MULTICAST_LOOP /* * Don't send back to itself, but allow it to fail to set it */ if (setsockopt(iface->fd, IPPROTO_IP, IP_MULTICAST_LOOP, SETSOCKOPT_ARG_CAST &off, sizeof(off)) == -1) { netsyslog(LOG_ERR, "setsockopt IP_MULTICAST_LOOP failure: %m on socket %d, addr %s for multicast address %s", iface->fd, stoa(&iface->sin), stoa(maddr)); } #endif DPRINTF(4, ("Added IPv4 multicast interface on socket %d, addr %s for multicast address %s\n", iface->fd, stoa(&iface->sin), stoa(maddr))); break; case AF_INET6: #ifdef INCLUDE_IPV6_MULTICAST_SUPPORT if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *) &iface->scopeid, sizeof(iface->scopeid)) == -1) { netsyslog(LOG_ERR, "setsockopt IPV6_MULTICAST_IF failure: %m on socket %d, addr %s, scope %d for multicast address %s", iface->fd, stoa(&iface->sin), iface->scopeid, stoa(maddr)); return; } #ifdef IPV6_MULTICAST_LOOP /* * Don't send back to itself, but allow it to fail to set it */ if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &off6, sizeof(off6)) == -1) { netsyslog(LOG_ERR, "setsockopt IPV6_MULTICAST_LOOP failure: %m on socket %d, addr %s for multicast address %s", iface->fd, stoa(&iface->sin), stoa(maddr)); } #endif DPRINTF(4, ("Added IPv6 multicast interface on socket %d, addr %s, scope %d for multicast address %s\n", iface->fd, stoa(&iface->sin), iface->scopeid, stoa(maddr))); break; #else return; #endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */ } return; #endif } /* * Add a multicast address to a given socket * The socket is in the inter_list all we need to do is enable * multicasting. It is not this function's job to select the socket */ static isc_boolean_t socket_multicast_enable(struct interface *iface, int lscope, struct sockaddr_storage *maddr) { #ifdef INCLUDE_IPV6_MULTICAST_SUPPORT struct ipv6_mreq mreq6; struct in6_addr iaddr6; #endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */ struct ip_mreq mreq; if (find_addr_in_list(maddr)) { DPRINTF(4, ("socket_multicast_enable(%s): already enabled\n", stoa(maddr))); return ISC_TRUE; } switch (maddr->ss_family) { case AF_INET: memset((char *)&mreq, 0, sizeof(mreq)); mreq.imr_multiaddr = (((struct sockaddr_in*)maddr)->sin_addr); mreq.imr_interface.s_addr = htonl(INADDR_ANY); if (setsockopt(iface->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) == -1) { netsyslog(LOG_ERR, "setsockopt IP_ADD_MEMBERSHIP failure: %m on socket %d, addr %s for %x / %x (%s)", iface->fd, stoa(&iface->sin), mreq.imr_multiaddr.s_addr, mreq.imr_interface.s_addr, stoa(maddr)); return ISC_FALSE; } DPRINTF(4, ("Added IPv4 multicast membership on socket %d, addr %s for %x / %x (%s)\n", iface->fd, stoa(&iface->sin), mreq.imr_multiaddr.s_addr, mreq.imr_interface.s_addr, stoa(maddr))); break; case AF_INET6: #ifdef INCLUDE_IPV6_MULTICAST_SUPPORT /* * Enable reception of multicast packets * If the address is link-local we can get the interface index * from the scope id. Don't do this for other types of multicast * addresses. For now let the kernel figure it out. */ memset((char *)&mreq6, 0, sizeof(mreq6)); iaddr6 = ((struct sockaddr_in6*)maddr)->sin6_addr; mreq6.ipv6mr_multiaddr = iaddr6; mreq6.ipv6mr_interface = lscope; if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&mreq6, sizeof(mreq6)) == -1) { netsyslog(LOG_ERR, "setsockopt IPV6_JOIN_GROUP failure: %m on socket %d, addr %s for interface %d(%s)", iface->fd, stoa(&iface->sin), mreq6.ipv6mr_interface, stoa(maddr)); return ISC_FALSE; } DPRINTF(4, ("Added IPv6 multicast group on socket %d, addr %s for interface %d(%s)\n", iface->fd, stoa(&iface->sin), mreq6.ipv6mr_interface, stoa(maddr))); break; #else return ISC_FALSE; #endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */ } iface->flags |= INT_MCASTOPEN; iface->num_mcast++; add_addr_to_list(maddr, iface); return ISC_TRUE; } /* * Remove a multicast address from a given socket * The socket is in the inter_list all we need to do is disable * multicasting. It is not this function's job to select the socket */ static isc_boolean_t socket_multicast_disable(struct interface *iface, struct sockaddr_storage *maddr) { #ifdef INCLUDE_IPV6_MULTICAST_SUPPORT struct ipv6_mreq mreq6; struct in6_addr iaddr6; #endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */ struct ip_mreq mreq; memset((char *)&mreq, 0, sizeof(mreq)); if (find_addr_in_list(maddr) == NULL) { DPRINTF(4, ("socket_multicast_disable(%s): not enabled\n", stoa(maddr))); return ISC_TRUE; } switch (maddr->ss_family) { case AF_INET: mreq.imr_multiaddr = (((struct sockaddr_in*)&maddr)->sin_addr); mreq.imr_interface.s_addr = ((struct sockaddr_in*)&iface->sin)->sin_addr.s_addr; if (setsockopt(iface->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) == -1) { netsyslog(LOG_ERR, "setsockopt IP_DROP_MEMBERSHIP failure: %m on socket %d, addr %s for %x / %x (%s)", iface->fd, stoa(&iface->sin), mreq.imr_multiaddr.s_addr, mreq.imr_interface.s_addr, stoa(maddr)); return ISC_FALSE; } break; case AF_INET6: #ifdef INCLUDE_IPV6_MULTICAST_SUPPORT /* * Disable reception of multicast packets * If the address is link-local we can get the interface index * from the scope id. Don't do this for other types of multicast * addresses. For now let the kernel figure it out. */ iaddr6 = ((struct sockaddr_in6*)&maddr)->sin6_addr; mreq6.ipv6mr_multiaddr = iaddr6; mreq6.ipv6mr_interface = iface->scopeid; if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, (char *)&mreq6, sizeof(mreq6)) == -1) { netsyslog(LOG_ERR, "setsockopt IPV6_LEAVE_GROUP failure: %m on socket %d, addr %s for %d(%s)", iface->fd, stoa(&iface->sin), mreq6.ipv6mr_interface, stoa(maddr)); return ISC_FALSE; } break; #else return ISC_FALSE; #endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */ } iface->num_mcast--; if (iface->num_mcast <= 0) { iface->num_mcast = 0; iface->flags &= ~INT_MCASTOPEN; } return ISC_TRUE; } /* * io_setbclient - open the broadcast client sockets */ void io_setbclient(void) { #ifdef OPEN_BCAST_SOCKET struct interface *interf; int nif = 0; isc_boolean_t jstatus; SOCKET fd; set_reuseaddr(1); for (interf = ISC_LIST_HEAD(inter_list); interf != NULL; interf = ISC_LIST_NEXT(interf, link)) { if (interf->flags & INT_WILDCARD) continue; /* use only allowed addresses */ if (interf->ignore_packets == ISC_TRUE) continue; /* Only IPv4 addresses are valid for broadcast */ if (interf->sin.ss_family != AF_INET) continue; /* Is this a broadcast address? */ if (!(interf->flags & INT_BROADCAST)) continue; /* Skip the loopback addresses */ if (interf->flags & INT_LOOPBACK) continue; /* Do we already have the broadcast address open? */ if (interf->flags & INT_BCASTOPEN) { /* account for already open interfaces to aviod misleading warning below */ nif++; continue; } /* * Try to open the broadcast address */ interf->family = AF_INET; interf->bfd = open_socket(&interf->bcast, INT_BROADCAST, 0, interf); /* * If we succeeded then we use it otherwise * enable the underlying address */ if (interf->bfd == INVALID_SOCKET) { fd = interf->fd; } else { fd = interf->bfd; } /* Enable Broadcast on socket */ jstatus = socket_broadcast_enable(interf, fd, &interf->sin); if (jstatus == ISC_TRUE) { nif++; netsyslog(LOG_INFO,"io_setbclient: Opened broadcast client on interface #%d %s, socket: %d", interf->ifnum, interf->name, fd); interf->addr_refid = addr2refid(&interf->sin); } } set_reuseaddr(0); #ifdef DEBUG if (debug) if (nif > 0) printf("io_setbclient: Opened broadcast clients\n"); #endif if (nif == 0) netsyslog(LOG_ERR, "Unable to listen for broadcasts, no broadcast interfaces available"); #else netsyslog(LOG_ERR, "io_setbclient: Broadcast Client disabled by build"); #endif } /* * io_unsetbclient - close the broadcast client sockets */ void io_unsetbclient(void) { struct interface *interf; isc_boolean_t lstatus; for (interf = ISC_LIST_HEAD(inter_list); interf != NULL; interf = ISC_LIST_NEXT(interf, link)) { if (interf->flags & INT_WILDCARD) continue; if (!(interf->flags & INT_BCASTOPEN)) continue; lstatus = socket_broadcast_disable(interf, &interf->sin); } } /* * io_multicast_add() - add multicast group address */ void io_multicast_add( struct sockaddr_storage addr ) { #ifdef MCAST struct interface *interface; #ifndef MULTICAST_NONEWSOCKET struct interface *iface; #endif int lscope = 0; /* * Check to see if this is a multicast address */ if (addr_ismulticast(&addr) == ISC_FALSE) return; /* If we already have it we can just return */ if (find_flagged_addr_in_list(&addr, INT_MCASTOPEN|INT_MCASTIF) != NULL) { netsyslog(LOG_INFO, "Duplicate request found for multicast address %s", stoa(&addr)); return; } #ifndef MULTICAST_NONEWSOCKET interface = new_interface(NULL); /* * Open a new socket for the multicast address */ interface->sin.ss_family = addr.ss_family; interface->family = addr.ss_family; switch(addr.ss_family) { case AF_INET: memcpy(&(((struct sockaddr_in *)&interface->sin)->sin_addr), &(((struct sockaddr_in*)&addr)->sin_addr), sizeof(struct in_addr)); ((struct sockaddr_in*)&interface->sin)->sin_port = htons(NTP_PORT); memset(&((struct sockaddr_in*)&interface->mask)->sin_addr.s_addr, 0xff, sizeof(struct in_addr)); break; case AF_INET6: #ifdef INCLUDE_IPV6_MULTICAST_SUPPORT memcpy(&(((struct sockaddr_in6 *)&interface->sin)->sin6_addr), &((struct sockaddr_in6*)&addr)->sin6_addr, sizeof(struct in6_addr)); ((struct sockaddr_in6*)&interface->sin)->sin6_port = htons(NTP_PORT); #ifdef ISC_PLATFORM_HAVESCOPEID ((struct sockaddr_in6*)&interface->sin)->sin6_scope_id = ((struct sockaddr_in6*)&addr)->sin6_scope_id; #endif memset(&((struct sockaddr_in6*)&interface->mask)->sin6_addr.s6_addr, 0xff, sizeof(struct in6_addr)); #endif iface = findlocalcastinterface(&addr, INT_MULTICAST); if (iface) { # ifdef ISC_PLATFORM_HAVESCOPEID lscope = ((struct sockaddr_in6*)&iface->sin)->sin6_scope_id; # endif DPRINTF(4, ("Found interface #%d %s, scope: %d for address %s\n", iface->ifnum, iface->name, lscope, stoa(&addr))); } break; } set_reuseaddr(1); interface->bfd = INVALID_SOCKET; interface->fd = open_socket(&interface->sin, INT_MULTICAST, 0, interface); if (interface->fd != INVALID_SOCKET) { interface->bfd = INVALID_SOCKET; interface->ignore_packets = ISC_FALSE; interface->flags |= INT_MCASTIF; (void) strncpy(interface->name, "multicast", sizeof(interface->name)); ((struct sockaddr_in*)&interface->mask)->sin_addr.s_addr = htonl(~(u_int32)0); DPRINT_INTERFACE(2, (interface, "multicast add ", "\n")); /* socket_multicast_enable() will add this address to the addresslist */ add_interface(interface); list_if_listening(interface, htons(NTP_PORT)); } else { delete_interface(interface); /* re-use existing interface */ interface = NULL; if (addr.ss_family == AF_INET) interface = wildipv4; else if (addr.ss_family == AF_INET6) interface = wildipv6; if (interface != NULL) { /* HACK ! -- stuff in an address */ interface->bcast = addr; netsyslog(LOG_ERR, "...multicast address %s using wildcard interface #%d %s", stoa(&addr), interface->ifnum, interface->name); } else { netsyslog(LOG_ERR, "No multicast socket available to use for address %s", stoa(&addr)); return; } } #else /* * For the case where we can't use a separate socket */ interface = findlocalcastinterface(&addr, INT_MULTICAST); /* * If we don't have a valid socket, just return */ if (!interface) { netsyslog(LOG_ERR, "Cannot add multicast address %s: Cannot find slot", stoa(&addr)); return; } #endif { isc_boolean_t jstatus; jstatus = socket_multicast_enable(interface, lscope, &addr); if (jstatus == ISC_TRUE) netsyslog(LOG_INFO, "Added Multicast Listener %s on interface #%d %s\n", stoa(&addr), interface->ifnum, interface->name); else netsyslog(LOG_ERR, "Failed to add Multicast Listener %s\n", stoa(&addr)); } #else /* MCAST */ netsyslog(LOG_ERR, "Cannot add multicast address %s: no Multicast support", stoa(&addr)); #endif /* MCAST */ return; } /* * io_multicast_del() - delete multicast group address */ void io_multicast_del( struct sockaddr_storage addr ) { #ifdef MCAST struct interface *interface; isc_boolean_t lstatus; /* * Check to see if this is a multicast address */ if (addr_ismulticast(&addr) == ISC_FALSE) { netsyslog(LOG_ERR, "invalid multicast address %s", stoa(&addr)); return; } switch (addr.ss_family) { case AF_INET : /* * Disable reception of multicast packets */ interface = find_flagged_addr_in_list(&addr, INT_MCASTOPEN); while ( interface != NULL) { lstatus = socket_multicast_disable(interface, &addr); interface = find_flagged_addr_in_list(&addr, INT_MCASTOPEN); } break; #ifdef INCLUDE_IPV6_MULTICAST_SUPPORT case AF_INET6 : /* * Disable reception of multicast packets */ for (interface = ISC_LIST_HEAD(inter_list); interface != NULL; interface = ISC_LIST_NEXT(interface, link)) { if (interface->flags & INT_WILDCARD) continue; /* Be sure it's the correct family */ if (interface->sin.ss_family != AF_INET6) continue; if (!(interface->flags & INT_MCASTOPEN)) continue; if (!(interface->fd < 0)) continue; if (!SOCKCMP(&addr, &interface->sin)) continue; lstatus = socket_multicast_disable(interface, &addr); } break; #endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */ }/* switch */ delete_addr_from_list(&addr); #else /* not MCAST */ netsyslog(LOG_ERR, "this function requires multicast kernel"); #endif /* not MCAST */ } /* * init_nonblocking_io() - set up descriptor to be non blocking */ static void init_nonblocking_io(SOCKET fd) { /* * set non-blocking, */ #ifdef USE_FIONBIO /* in vxWorks we use FIONBIO, but the others are defined for old systems, so * all hell breaks loose if we leave them defined */ #undef O_NONBLOCK #undef FNDELAY #undef O_NDELAY #endif #if defined(O_NONBLOCK) /* POSIX */ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { netsyslog(LOG_ERR, "fcntl(O_NONBLOCK) fails on fd #%d: %m", fd); exit(1); /*NOTREACHED*/ } #elif defined(FNDELAY) if (fcntl(fd, F_SETFL, FNDELAY) < 0) { netsyslog(LOG_ERR, "fcntl(FNDELAY) fails on fd #%d: %m", fd); exit(1); /*NOTREACHED*/ } #elif defined(O_NDELAY) /* generally the same as FNDELAY */ if (fcntl(fd, F_SETFL, O_NDELAY) < 0) { netsyslog(LOG_ERR, "fcntl(O_NDELAY) fails on fd #%d: %m", fd); exit(1); /*NOTREACHED*/ } #elif defined(FIONBIO) { int on = 1; if (ioctl(fd,FIONBIO,&on) < 0) { netsyslog(LOG_ERR, "ioctl(FIONBIO) fails on fd #%d: %m", fd); exit(1); /*NOTREACHED*/ } } #elif defined(FIOSNBIO) if (ioctl(fd,FIOSNBIO,&on) < 0) { netsyslog(LOG_ERR, "ioctl(FIOSNBIO) fails on fd #%d: %m", fd); exit(1); /*NOTREACHED*/ } #else # include "Bletch: Need non-blocking I/O!" #endif } /* * open_socket - open a socket, returning the file descriptor */ static SOCKET open_socket( struct sockaddr_storage *addr, int flags, int turn_off_reuse, struct interface *interf ) { int errval; SOCKET fd; /* * int is OK for REUSEADR per * http://www.kohala.com/start/mcast.api.txt */ int on = 1; int off = 0; #if defined(IPTOS_LOWDELAY) && defined(IPPROTO_IP) && defined(IP_TOS) int tos; #endif /* IPTOS_LOWDELAY && IPPROTO_IP && IP_TOS */ if ((addr->ss_family == AF_INET6) && (isc_net_probeipv6() != ISC_R_SUCCESS)) return (INVALID_SOCKET); /* create a datagram (UDP) socket */ fd = socket(addr->ss_family, SOCK_DGRAM, 0); if (INVALID_SOCKET == fd) { #ifndef SYS_WINNT errval = errno; #else errval = WSAGetLastError(); #endif netsyslog(LOG_ERR, "socket(AF_INET%s, SOCK_DGRAM, 0) failed on address %s: %m", (addr->ss_family == AF_INET6) ? "6" : "", stoa(addr)); if (errval == EPROTONOSUPPORT || errval == EAFNOSUPPORT || errval == EPFNOSUPPORT) return (INVALID_SOCKET); msyslog(LOG_ERR, "unexpected error code %d (not PROTONOSUPPORT|AFNOSUPPORT|FPNOSUPPORT) - exiting", errval); exit(1); /*NOTREACHED*/ } #ifdef SYS_WINNT connection_reset_fix(fd, addr); #endif /* * Fixup the file descriptor for some systems * See bug #530 for details of the issue. */ fd = move_fd(fd); /* * set SO_REUSEADDR since we will be binding the same port * number on each interface according to turn_off_reuse. * This is undesirable on Windows versions starting with * Windows XP (numeric version 5.1). */ #ifdef SYS_WINNT if (isc_win32os_versioncheck(5, 1, 0, 0) < 0) /* before 5.1 */ #endif if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)(turn_off_reuse ? &off : &on), sizeof(on))) { netsyslog(LOG_ERR, "setsockopt SO_REUSEADDR %s" " fails for address %s: %m", turn_off_reuse ? "off" : "on", stoa(addr)); closesocket(fd); return INVALID_SOCKET; } #ifdef SO_EXCLUSIVEADDRUSE /* * setting SO_EXCLUSIVEADDRUSE on the wildcard we open * first will cause more specific binds to fail. */ if (!(interf->flags & INT_WILDCARD)) set_excladdruse(fd); #endif /* * IPv4 specific options go here */ if (addr->ss_family == AF_INET) { #if defined(IPTOS_LOWDELAY) && defined(IPPROTO_IP) && defined(IP_TOS) /* set IP_TOS to minimize packet delay */ tos = IPTOS_LOWDELAY; if (setsockopt(fd, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(tos)) < 0) { netsyslog(LOG_ERR, "setsockopt IPTOS_LOWDELAY on fails on address %s: %m", stoa(addr)); } #endif /* IPTOS_LOWDELAY && IPPROTO_IP && IP_TOS */ } /* * IPv6 specific options go here */ if (addr->ss_family == AF_INET6) { #if defined(IPV6_V6ONLY) if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&on, sizeof(on))) { netsyslog(LOG_ERR, "setsockopt IPV6_V6ONLY on fails on address %s: %m", stoa(addr)); } #endif /* IPV6_V6ONLY */ #if defined(IPV6_BINDV6ONLY) if (setsockopt(fd, IPPROTO_IPV6, IPV6_BINDV6ONLY, (char*)&on, sizeof(on))) { netsyslog(LOG_ERR, "setsockopt IPV6_BINDV6ONLY on fails on address %s: %m", stoa(addr)); } #endif /* IPV6_BINDV6ONLY */ } #ifdef OS_NEEDS_REUSEADDR_FOR_IFADDRBIND /* * some OSes don't allow binding to more specific * addresses if a wildcard address already bound * to the port and SO_REUSEADDR is not set */ if (!is_wildcard_addr(addr)) { set_wildcard_reuse(addr->ss_family, 1); } #endif /* * bind the local address. */ errval = bind(fd, (struct sockaddr *)addr, SOCKLEN(addr)); #ifdef OS_NEEDS_REUSEADDR_FOR_IFADDRBIND /* * some OSes don't allow binding to more specific * addresses if a wildcard address already bound * to the port and REUSE_ADDR is not set */ if (!is_wildcard_addr(addr)) { set_wildcard_reuse(addr->ss_family, 0); } #endif if (errval < 0) { /* * Don't log this under all conditions */ if (turn_off_reuse == 0 #ifdef DEBUG || debug > 1 #endif ) { if (addr->ss_family == AF_INET) netsyslog(LOG_ERR, "bind() fd %d, family AF_INET, port %d, addr %s, in_classd=%d flags=0x%x fails: %m", fd, (int)ntohs(((struct sockaddr_in*)addr)->sin_port), stoa(addr), IN_CLASSD(ntohl(((struct sockaddr_in*)addr)->sin_addr.s_addr)), flags); #ifdef INCLUDE_IPV6_SUPPORT else if (addr->ss_family == AF_INET6) netsyslog(LOG_ERR, "bind() fd %d, family AF_INET6, port %d, scope %d, addr %s, mcast=%d flags=0x%x fails: %m", fd, (int)ntohs(((struct sockaddr_in6*)addr)->sin6_port), # ifdef ISC_PLATFORM_HAVESCOPEID ((struct sockaddr_in6*)addr)->sin6_scope_id # else -1 # endif , stoa(addr), IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)addr)->sin6_addr), flags); #endif } closesocket(fd); return INVALID_SOCKET; } #ifdef HAVE_TIMESTAMP { if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, (char*)&on, sizeof(on))) { netsyslog(LOG_DEBUG, "setsockopt SO_TIMESTAMP on fails on address %s: %m", stoa(addr)); } #ifdef DEBUG else { DPRINTF(4, ("setsockopt SO_TIMESTAMP enabled on fd %d address %s\n", fd, stoa(addr))); } #endif } #endif DPRINTF(4, ("bind() fd %d, family %d, port %d, addr %s, flags=0x%x\n", fd, addr->ss_family, (int)ntohs(((struct sockaddr_in*)addr)->sin_port), stoa(addr), interf->flags)); init_nonblocking_io(fd); #ifdef HAVE_SIGNALED_IO init_socket_sig(fd); #endif /* not HAVE_SIGNALED_IO */ add_fd_to_list(fd, FD_TYPE_SOCKET); #if !defined(SYS_WINNT) && !defined(VMS) DPRINTF(4, ("flags for fd %d: 0x%x\n", fd, fcntl(fd, F_GETFL, 0))); #endif /* SYS_WINNT || VMS */ #if defined (HAVE_IO_COMPLETION_PORT) /* * Add the socket to the completion port */ if (io_completion_port_add_socket(fd, interf)) { msyslog(LOG_ERR, "unable to set up io completion port - EXITING"); exit(1); } #endif return fd; } /* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */ /* * sendpkt - send a packet to the specified destination. Maintain a * send error cache so that only the first consecutive error for a * destination is logged. */ void sendpkt( struct sockaddr_storage *dest, struct interface *inter, int ttl, struct pkt *pkt, int len ) { int cc, slot; /* * Send error caches. Empty slots have port == 0 * Set ERRORCACHESIZE to 0 to disable */ struct cache { u_short port; struct in_addr addr; }; #ifdef INCLUDE_IPV6_SUPPORT struct cache6 { u_short port; struct in6_addr addr; }; #endif /* INCLUDE_IPV6_SUPPORT */ #ifndef ERRORCACHESIZE #define ERRORCACHESIZE 8 #endif #if ERRORCACHESIZE > 0 static struct cache badaddrs[ERRORCACHESIZE]; #ifdef INCLUDE_IPV6_SUPPORT static struct cache6 badaddrs6[ERRORCACHESIZE]; #endif /* INCLUDE_IPV6_SUPPORT */ #else #define badaddrs ((struct cache *)0) /* Only used in empty loops! */ #ifdef INCLUDE_IPV6_SUPPORT #define badaddrs6 ((struct cache6 *)0) /* Only used in empty loops! */ #endif /* INCLUDE_IPV6_SUPPORT */ #endif #ifdef DEBUG if (debug > 1) { if (inter != NULL) { printf("%ssendpkt(fd=%d dst=%s, src=%s, ttl=%d, len=%d)\n", (ttl > 0) ? "\tMCAST\t***** " : "", inter->fd, stoa(dest), stoa(&inter->sin), ttl, len); } else { printf("%ssendpkt(dst=%s, ttl=%d, len=%d): no interface - IGNORED\n", (ttl > 0) ? "\tMCAST\t***** " : "", stoa(dest), ttl, len); } } #endif if (inter == NULL) /* unbound peer - drop request and wait for better network conditions */ return; #ifdef MCAST /* * for the moment we use the bcast option to set multicast ttl */ if (ttl > 0 && ttl != inter->last_ttl) { /* * set the multicast ttl for outgoing packets */ int rtc; switch (inter->sin.ss_family) { case AF_INET : { u_char mttl = (u_char) ttl; rtc = setsockopt(inter->fd, IPPROTO_IP, IP_MULTICAST_TTL, (const void *) &mttl, sizeof(mttl)); break; } #ifdef INCLUDE_IPV6_SUPPORT case AF_INET6 : { u_int ittl = (u_char) ttl; rtc = setsockopt(inter->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const void *) &ittl, sizeof(ittl)); break; } #endif /* INCLUDE_IPV6_SUPPORT */ default: /* just NOP if not supported */ rtc = 0; break; } if (rtc != 0) { netsyslog(LOG_ERR, "setsockopt IP_MULTICAST_TTL/IPV6_MULTICAST_HOPS fails on address %s: %m", stoa(&inter->sin)); } else inter->last_ttl = ttl; } #endif /* MCAST */ for (slot = ERRORCACHESIZE; --slot >= 0; ) if(dest->ss_family == AF_INET) { if (badaddrs[slot].port == SRCPORT(dest) && badaddrs[slot].addr.s_addr == ((struct sockaddr_in*)dest)->sin_addr.s_addr) break; } #ifdef INCLUDE_IPV6_SUPPORT else if (dest->ss_family == AF_INET6) { if (badaddrs6[slot].port == SRCPORT(dest) && !memcmp(&badaddrs6[slot].addr, &((struct sockaddr_in6*)dest)->sin6_addr, sizeof(struct in6_addr))) break; } #endif /* INCLUDE_IPV6_SUPPORT */ #if defined(HAVE_IO_COMPLETION_PORT) cc = io_completion_port_sendto(inter, pkt, len, dest); if (cc != ERROR_SUCCESS) #else #ifdef SIM cc = srvr_rply(&ntp_node, dest, inter, pkt); #else /* SIM */ cc = sendto(inter->fd, (char *)pkt, (unsigned int)len, 0, (struct sockaddr *)dest, SOCKLEN(dest)); #endif /* SIM */ if (cc == -1) #endif { inter->notsent++; packets_notsent++; #if defined(HAVE_IO_COMPLETION_PORT) if (cc != WSAEWOULDBLOCK && cc != WSAENOBUFS && slot < 0) #else if (errno != EWOULDBLOCK && errno != ENOBUFS && slot < 0) #endif { /* * Remember this, if there's an empty slot */ switch (dest->ss_family) { case AF_INET : for (slot = ERRORCACHESIZE; --slot >= 0; ) if (badaddrs[slot].port == 0) { badaddrs[slot].port = SRCPORT(dest); badaddrs[slot].addr = ((struct sockaddr_in*)dest)->sin_addr; break; } break; #ifdef INCLUDE_IPV6_SUPPORT case AF_INET6 : for (slot = ERRORCACHESIZE; --slot >= 0; ) if (badaddrs6[slot].port == 0) { badaddrs6[slot].port = SRCPORT(dest); badaddrs6[slot].addr = ((struct sockaddr_in6*)dest)->sin6_addr; break; } break; #endif /* INCLUDE_IPV6_SUPPORT */ default: /* don't care if not supported */ break; } netsyslog(LOG_ERR, "sendto(%s) (fd=%d): %m", stoa(dest), inter->fd); } } else { inter->sent++; packets_sent++; /* * He's not bad any more */ if (slot >= 0) { netsyslog(LOG_INFO, "Connection re-established to %s", stoa(dest)); switch (dest->ss_family) { case AF_INET : badaddrs[slot].port = 0; break; #ifdef INCLUDE_IPV6_SUPPORT case AF_INET6 : badaddrs6[slot].port = 0; break; #endif /* INCLUDE_IPV6_SUPPORT */ default: /* don't care if not supported */ break; } } } } #if !defined(HAVE_IO_COMPLETION_PORT) /* * fdbits - generate ascii representation of fd_set (FAU debug support) * HFDF format - highest fd first. */ static char * fdbits( int count, fd_set *set ) { static char buffer[256]; char * buf = buffer; count = (count < 256) ? count : 255; while (count >= 0) { *buf++ = FD_ISSET(count, set) ? '#' : '-'; count--; } *buf = '\0'; return buffer; } /* * Routine to read the refclock packets for a specific interface * Return the number of bytes read. That way we know if we should * read it again or go on to the next one if no bytes returned */ static inline int read_refclock_packet(SOCKET fd, struct refclockio *rp, l_fp ts) { int i; int buflen; register struct recvbuf *rb; rb = get_free_recv_buffer(); if (rb == NULL) { /* * No buffer space available - just drop the packet */ char buf[RX_BUFF_SIZE]; buflen = read(fd, buf, sizeof buf); packets_dropped++; return (buflen); } i = (rp->datalen == 0 || rp->datalen > sizeof(rb->recv_space)) ? sizeof(rb->recv_space) : rp->datalen; buflen = read(fd, (char *)&rb->recv_space, (unsigned)i); if (buflen < 0) { if (errno != EINTR && errno != EAGAIN) { netsyslog(LOG_ERR, "clock read fd %d: %m", fd); } freerecvbuf(rb); return (buflen); } /* * Got one. Mark how and when it got here, * put it on the full list and do bookkeeping. */ rb->recv_length = buflen; rb->recv_srcclock = rp->srcclock; rb->dstadr = 0; rb->fd = fd; rb->recv_time = ts; rb->receiver = rp->clock_recv; if (rp->io_input) { /* * have direct input routine for refclocks */ if (rp->io_input(rb) == 0) { /* * data was consumed - nothing to pass up * into block input machine */ freerecvbuf(rb); return (buflen); } } add_full_recv_buffer(rb); rp->recvcount++; packets_received++; return (buflen); } #ifdef HAVE_TIMESTAMP /* * extract timestamps from control message buffer */ static l_fp fetch_timestamp(struct recvbuf *rb, struct msghdr *msghdr, l_fp ts) { #ifdef USE_TIMESTAMP_CMSG struct cmsghdr *cmsghdr; cmsghdr = CMSG_FIRSTHDR(msghdr); while (cmsghdr != NULL) { switch (cmsghdr->cmsg_type) { case SCM_TIMESTAMP: { struct timeval *tvp = (struct timeval *)CMSG_DATA(cmsghdr); double dtemp; l_fp nts; DPRINTF(4, ("fetch_timestamp: system network time stamp: %ld.%06ld\n", tvp->tv_sec, tvp->tv_usec)); nts.l_i = tvp->tv_sec + JAN_1970; dtemp = tvp->tv_usec / 1e6; /* fuzz lower bits not covered by precision */ if (sys_precision != 0) dtemp += (ntp_random() / FRAC - .5) / (1 << -sys_precision); nts.l_uf = (u_int32)(dtemp*FRAC); #ifdef DEBUG_TIMING { l_fp dts = ts; L_SUB(&dts, &nts); collect_timing(rb, "input processing delay", 1, &dts); DPRINTF(4, ("fetch_timestamp: timestamp delta: %s (incl. prec fuzz)\n", lfptoa(&dts, 9))); } #endif ts = nts; /* network time stamp */ break; } default: DPRINTF(4, ("fetch_timestamp: skipping control message 0x%x\n", cmsghdr->cmsg_type)); break; } cmsghdr = CMSG_NXTHDR(msghdr, cmsghdr); } #endif return ts; } #endif /* * Routine to read the network NTP packets for a specific interface * Return the number of bytes read. That way we know if we should * read it again or go on to the next one if no bytes returned */ static inline int read_network_packet(SOCKET fd, struct interface *itf, l_fp ts) { GETSOCKNAME_SOCKLEN_TYPE fromlen; int buflen; register struct recvbuf *rb; #ifdef HAVE_TIMESTAMP struct msghdr msghdr; struct iovec iovec; char control[TIMESTAMP_CTLMSGBUF_SIZE]; /* pick up control messages */ #endif /* * Get a buffer and read the frame. If we * haven't got a buffer, or this is received * on a disallowed socket, just dump the * packet. */ rb = get_free_recv_buffer(); if (rb == NULL || itf->ignore_packets == ISC_TRUE) { char buf[RX_BUFF_SIZE]; struct sockaddr_storage from; if (rb != NULL) freerecvbuf(rb); fromlen = sizeof(from); buflen = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&from, &fromlen); DPRINTF(4, ("%s on (%lu) fd=%d from %s\n", (itf->ignore_packets == ISC_TRUE) ? "ignore" : "drop", free_recvbuffs(), fd, stoa(&from))); if (itf->ignore_packets == ISC_TRUE) packets_ignored++; else packets_dropped++; return (buflen); } fromlen = sizeof(struct sockaddr_storage); #ifndef HAVE_TIMESTAMP rb->recv_length = recvfrom(fd, (char *)&rb->recv_space, sizeof(rb->recv_space), 0, (struct sockaddr *)&rb->recv_srcadr, &fromlen); #else iovec.iov_base = (void *)&rb->recv_space; iovec.iov_len = sizeof(rb->recv_space); msghdr.msg_name = (void *)&rb->recv_srcadr; msghdr.msg_namelen = sizeof(rb->recv_srcadr); msghdr.msg_iov = &iovec; msghdr.msg_iovlen = 1; msghdr.msg_control = (void *)&control; msghdr.msg_controllen = sizeof(control); msghdr.msg_flags = 0; rb->recv_length = recvmsg(fd, &msghdr, 0); #endif buflen = rb->recv_length; if (buflen == 0 || (buflen == -1 && (errno==EWOULDBLOCK #ifdef EAGAIN || errno==EAGAIN #endif ))) { freerecvbuf(rb); return (buflen); } else if (buflen < 0) { netsyslog(LOG_ERR, "recvfrom(%s) fd=%d: %m", stoa(&rb->recv_srcadr), fd); DPRINTF(5, ("read_network_packet: fd=%d dropped (bad recvfrom)\n", fd)); freerecvbuf(rb); return (buflen); } #ifdef DEBUG if (debug > 2) { if(rb->recv_srcadr.ss_family == AF_INET) printf("read_network_packet: fd=%d length %d from %08lx %s\n", fd, buflen, (u_long)ntohl(((struct sockaddr_in*)&rb->recv_srcadr)->sin_addr.s_addr) & 0x00000000ffffffff, stoa(&rb->recv_srcadr)); else printf("read_network_packet: fd=%d length %d from %s\n", fd, buflen, stoa(&rb->recv_srcadr)); } #endif /* * Got one. Mark how and when it got here, * put it on the full list and do bookkeeping. */ rb->dstadr = itf; rb->fd = fd; #ifdef HAVE_TIMESTAMP ts = fetch_timestamp(rb, &msghdr, ts); /* pick up a network time stamp if possible */ #endif rb->recv_time = ts; rb->receiver = receive; add_full_recv_buffer(rb); itf->received++; packets_received++; return (buflen); } /* * input_handler - receive packets asynchronously */ void input_handler( l_fp *cts ) { int buflen; int n; int doing; SOCKET fd; struct timeval tvzero; l_fp ts; /* Timestamp at BOselect() gob */ #ifdef DEBUG_TIMING l_fp ts_e; /* Timestamp at EOselect() gob */ #endif fd_set fds; int select_count = 0; struct interface *interface; #if defined(HAS_ROUTING_SOCKET) struct asyncio_reader *asyncio_reader; #endif handler_calls++; /* * If we have something to do, freeze a timestamp. * See below for the other cases (nothing (left) to do or error) */ ts = *cts; /* * Do a poll to see who has data */ fds = activefds; tvzero.tv_sec = tvzero.tv_usec = 0; n = select(maxactivefd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero); /* * If there are no packets waiting just return */ if (n < 0) { int err = errno; /* * extended FAU debugging output */ if (err != EINTR) netsyslog(LOG_ERR, "select(%d, %s, 0L, 0L, &0.0) error: %m", maxactivefd+1, fdbits(maxactivefd, &activefds)); if (err == EBADF) { int j, b; fds = activefds; for (j = 0; j <= maxactivefd; j++) if ((FD_ISSET(j, &fds) && (read(j, &b, 0) == -1))) netsyslog(LOG_ERR, "Bad file descriptor %d", j); } return; } else if (n == 0) return; ++handler_pkts; #ifdef REFCLOCK /* * Check out the reference clocks first, if any */ if (refio != NULL) { register struct refclockio *rp; for (rp = refio; rp != NULL; rp = rp->next) { fd = rp->fd; if (FD_ISSET(fd, &fds)) { do { ++select_count; buflen = read_refclock_packet(fd, rp, ts); } while (buflen > 0); } /* End if (FD_ISSET(fd, &fds)) */ } /* End for (rp = refio; rp != 0 && n > 0; rp = rp->next) */ } /* End if (refio != 0) */ #endif /* REFCLOCK */ /* * Loop through the interfaces looking for data to read. */ for (interface = ISC_LIST_TAIL(inter_list); interface != NULL; interface = ISC_LIST_PREV(interface, link)) { for (doing = 0; (doing < 2); doing++) { if (doing == 0) { fd = interface->fd; } else { if (!(interface->flags & INT_BCASTOPEN)) break; fd = interface->bfd; } if (fd < 0) continue; if (FD_ISSET(fd, &fds)) { do { ++select_count; buflen = read_network_packet(fd, interface, ts); } while (buflen > 0); } /* Check more interfaces */ } } #ifdef HAS_ROUTING_SOCKET /* * scan list of asyncio readers - currently only used for routing sockets */ asyncio_reader = ISC_LIST_TAIL(asyncio_reader_list); while (asyncio_reader != NULL) { struct asyncio_reader *next = ISC_LIST_PREV(asyncio_reader, link); if (FD_ISSET(asyncio_reader->fd, &fds)) { ++select_count; asyncio_reader->receiver(asyncio_reader); } asyncio_reader = next; } #endif /* HAS_ROUTING_SOCKET */ /* * Done everything from that select. */ /* * If nothing to do, just return. * If an error occurred, complain and return. */ if (select_count == 0) /* We really had nothing to do */ { #ifdef DEBUG if (debug) netsyslog(LOG_DEBUG, "input_handler: select() returned 0"); #endif return; } /* We've done our work */ #ifdef DEBUG_TIMING get_systime(&ts_e); /* * (ts_e - ts) is the amount of time we spent * processing this gob of file descriptors. Log * it. */ L_SUB(&ts_e, &ts); collect_timing(NULL, "input handler", 1, &ts_e); if (debug > 3) netsyslog(LOG_INFO, "input_handler: Processed a gob of fd's in %s msec", lfptoms(&ts_e, 6)); #endif /* just bail. */ return; } #endif /* * findinterface - find local interface corresponding to address */ struct interface * findinterface( struct sockaddr_storage *addr ) { struct interface *interface; interface = findlocalinterface(addr, INT_WILDCARD); if (interface == NULL) { DPRINTF(4, ("Found no interface for address %s - returning wildcard\n", stoa(addr))); return (ANY_INTERFACE_CHOOSE(addr)); } else { DPRINTF(4, ("Found interface #%d %s for address %s\n", interface->ifnum, interface->name, stoa(addr))); return (interface); } } /* * findlocalinterface - find local interface index corresponding to address * * This code attempts to find the local sending address for an outgoing * address by connecting a new socket to destinationaddress:NTP_PORT * and reading the sockname of the resulting connect. * the complicated sequence simulates the routing table lookup * for to first hop without duplicating any of the routing logic into * ntpd. preferably we would have used an API call - but its not there - * so this is the best we can do here short of duplicating to entire routing * logic in ntpd which would be a silly and really unportable thing to do. * */ static struct interface * findlocalinterface( struct sockaddr_storage *addr, int flags ) { SOCKET s; int rtn; struct sockaddr_storage saddr; GETSOCKNAME_SOCKLEN_TYPE saddrlen = SOCKLEN(addr); struct interface *iface; DPRINTF(4, ("Finding interface for addr %s in list of addresses\n", stoa(addr))); memset(&saddr, 0, sizeof(saddr)); saddr.ss_family = addr->ss_family; if(addr->ss_family == AF_INET) { memcpy(&((struct sockaddr_in*)&saddr)->sin_addr, &((struct sockaddr_in*)addr)->sin_addr, sizeof(struct in_addr)); ((struct sockaddr_in*)&saddr)->sin_port = htons(NTP_PORT); } #ifdef INCLUDE_IPV6_SUPPORT else if(addr->ss_family == AF_INET6) { memcpy(&((struct sockaddr_in6*)&saddr)->sin6_addr, &((struct sockaddr_in6*)addr)->sin6_addr, sizeof(struct in6_addr)); ((struct sockaddr_in6*)&saddr)->sin6_port = htons(NTP_PORT); # ifdef ISC_PLATFORM_HAVESCOPEID ((struct sockaddr_in6*)&saddr)->sin6_scope_id = ((struct sockaddr_in6*)addr)->sin6_scope_id; # endif } #endif s = socket(addr->ss_family, SOCK_DGRAM, 0); if (s == INVALID_SOCKET) return NULL; rtn = connect(s, (struct sockaddr *)&saddr, SOCKLEN(&saddr)); #ifndef SYS_WINNT if (rtn < 0) #else if (rtn == SOCKET_ERROR) #endif { closesocket(s); return NULL; } rtn = getsockname(s, (struct sockaddr *)&saddr, &saddrlen); closesocket(s); #ifndef SYS_WINNT if (rtn < 0) #else if (rtn == SOCKET_ERROR) #endif return NULL; DPRINTF(4, ("findlocalinterface: kernel maps %s to %s\n", stoa(addr), stoa(&saddr))); iface = getinterface(&saddr, flags); /* Don't both with ignore interfaces */ if (iface != NULL && iface->ignore_packets == ISC_TRUE) { return NULL; } else { return iface; } } /* * fetch an interface structure the matches the * address is has the given flags not set */ static struct interface * getinterface(struct sockaddr_storage *addr, int flags) { struct interface *interface = find_addr_in_list(addr); if (interface != NULL && interface->flags & flags) { return NULL; } else { return interface; } } /* * findlocalcastinterface - find local *cast interface index corresponding to address * depending on the flags passed */ static struct interface * findlocalcastinterface( struct sockaddr_storage *addr, int flags ) { struct interface *interface; struct interface *nif = NULL; #ifdef INCLUDE_IPV6_MULTICAST_SUPPORT isc_boolean_t want_linklocal; #endif /* * see how kernel maps the mcast address */ nif = findlocalinterface(addr, 0); if (nif) { DPRINTF(2, ("findlocalcastinterface: kernel recommends interface #%d %s\n", nif->ifnum, nif->name)); return nif; } #ifdef INCLUDE_IPV6_MULTICAST_SUPPORT want_linklocal = ISC_FALSE; if (addr_ismulticast(addr) && flags == INT_MULTICAST) { if (IN6_IS_ADDR_MC_LINKLOCAL(&((struct sockaddr_in6*)addr)->sin6_addr)) { want_linklocal = ISC_TRUE; } else if (IN6_IS_ADDR_MC_SITELOCAL(&((struct sockaddr_in6*)addr)->sin6_addr)) { want_linklocal = ISC_TRUE; } } #endif for (interface = ISC_LIST_HEAD(inter_list); interface != NULL; interface = ISC_LIST_NEXT(interface, link)) { /* use only allowed addresses */ if (interface->ignore_packets == ISC_TRUE) continue; /* Skip the loopback and wildcard addresses */ if (interface->flags & (INT_LOOPBACK|INT_WILDCARD)) continue; /* Skip if different family */ if(interface->sin.ss_family != addr->ss_family) continue; /* Is this it one of these based on flags? */ if (!(interface->flags & flags)) continue; /* for IPv6 multicast check the address for linklocal */ #ifdef INCLUDE_IPV6_MULTICAST_SUPPORT if (flags == INT_MULTICAST && interface->sin.ss_family == AF_INET6 && (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6*)&interface->sin)->sin6_addr)) && want_linklocal == ISC_TRUE) { nif = interface; break; } /* If we want a linklocal address and this isn't it, skip */\ if (want_linklocal == ISC_TRUE) continue; #endif /* Otherwise just look for the flag */ if((interface->flags & flags)) { nif = interface; break; } } #ifdef DEBUG if (debug > 2) { if (nif) printf("findlocalcastinterface: found interface #%d %s\n", nif->ifnum, nif->name); else printf("findlocalcastinterface: no interface found for %s flags 0x%x\n", stoa(addr), flags); } #endif return (nif); } /* * findbcastinter - find broadcast interface corresponding to address */ struct interface * findbcastinter( struct sockaddr_storage *addr ) { #if !defined(MPE) && (defined(SIOCGIFCONF) || defined(SYS_WINNT)) struct interface *interface; DPRINTF(4, ("Finding broadcast/multicast interface for addr %s in list of addresses\n", stoa(addr))); interface = findlocalinterface(addr, INT_LOOPBACK|INT_WILDCARD); if (interface != NULL) { DPRINTF(4, ("Found bcast-/mcast- interface index #%d %s\n", interface->ifnum, interface->name)); return interface; } /* plan B - try to find something reasonable in our lists in case kernel lookup doesn't help */ for (interface = ISC_LIST_HEAD(inter_list); interface != NULL; interface = ISC_LIST_NEXT(interface, link)) { if (interface->flags & INT_WILDCARD) continue; /* Don't bother with ignored interfaces */ if (interface->ignore_packets == ISC_TRUE) continue; /* * First look if this is the correct family */ if(interface->sin.ss_family != addr->ss_family) continue; /* Skip the loopback addresses */ if (interface->flags & INT_LOOPBACK) continue; /* * If we are looking to match a multicast address grab it. */ if (addr_ismulticast(addr) == ISC_TRUE && interface->flags & INT_MULTICAST) { #ifdef INCLUDE_IPV6_SUPPORT if(addr->ss_family == AF_INET6) { /* Only use link-local address for link-scope mcast */ if(IN6_IS_ADDR_MC_LINKLOCAL(&((struct sockaddr_in6*)addr)->sin6_addr) && !IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6*)&interface->sin)->sin6_addr)) { continue; } } #endif break; } /* * We match only those interfaces marked as * broadcastable and either the explicit broadcast * address or the network portion of the IP address. * Sloppy. */ if(addr->ss_family == AF_INET) { if (SOCKCMP(&interface->bcast, addr)) { break; } if ((NSRCADR(&interface->sin) & NSRCADR(&interface->mask)) == (NSRCADR(addr) & NSRCADR(&interface->mask))) break; } #ifdef INCLUDE_IPV6_SUPPORT else if(addr->ss_family == AF_INET6) { if (SOCKCMP(&interface->bcast, addr)) { break; } if (SOCKCMP(netof(&interface->sin), netof(addr))) { break; } } #endif } #endif /* SIOCGIFCONF */ if (interface == NULL) { DPRINTF(4, ("No bcast interface found for %s\n", stoa(addr))); return ANY_INTERFACE_CHOOSE(addr); } else { DPRINTF(4, ("Found bcast-/mcast- interface index #%d %s\n", interface->ifnum, interface->name)); return interface; } } /* * io_clr_stats - clear I/O module statistics */ void io_clr_stats(void) { packets_dropped = 0; packets_ignored = 0; packets_received = 0; packets_sent = 0; packets_notsent = 0; handler_calls = 0; handler_pkts = 0; io_timereset = current_time; } #ifdef REFCLOCK /* * io_addclock - add a reference clock to the list and arrange that we * get SIGIO interrupts from it. */ int io_addclock( struct refclockio *rio ) { BLOCKIO(); /* * Stuff the I/O structure in the list and mark the descriptor * in use. There is a harmless (I hope) race condition here. */ rio->next = refio; # ifdef HAVE_SIGNALED_IO if (init_clock_sig(rio)) { UNBLOCKIO(); return 0; } # elif defined(HAVE_IO_COMPLETION_PORT) if (io_completion_port_add_clock_io(rio)) { UNBLOCKIO(); return 0; } # endif /* * enqueue */ refio = rio; /* * register fd */ add_fd_to_list(rio->fd, FD_TYPE_FILE); UNBLOCKIO(); return 1; } /* * io_closeclock - close the clock in the I/O structure given */ void io_closeclock( struct refclockio *rio ) { BLOCKIO(); /* * Remove structure from the list */ if (refio == rio) { refio = rio->next; } else { register struct refclockio *rp; for (rp = refio; rp != NULL; rp = rp->next) if (rp->next == rio) { rp->next = rio->next; break; } if (rp == NULL) { UNBLOCKIO(); return; } } /* * Close the descriptor. */ close_and_delete_fd_from_list(rio->fd); UNBLOCKIO(); } #endif /* REFCLOCK */ /* * On NT a SOCKET is an unsigned int so we cannot possibly keep it in * an array. So we use one of the ISC_LIST functions to hold the * socket value and use that when we want to enumerate it. */ void kill_asyncio(int startfd) { vsock_t *lsock; vsock_t *next; BLOCKIO(); lsock = ISC_LIST_HEAD(fd_list); while (lsock != NULL) { /* * careful here - list is being dismantled while * we scan it - setting next here insures that * we are able to correctly scan the list */ next = ISC_LIST_NEXT(lsock, link); /* * will remove socket from list */ close_and_delete_fd_from_list(lsock->fd); lsock = next; } UNBLOCKIO(); } /* * Add and delete functions for the list of open sockets */ static void add_fd_to_list(SOCKET fd, enum desc_type type) { vsock_t *lsock = (vsock_t *)emalloc(sizeof(vsock_t)); lsock->fd = fd; lsock->type = type; ISC_LIST_APPEND(fd_list, lsock, link); /* * I/O Completion Ports don't care about the select and FD_SET */ #ifndef HAVE_IO_COMPLETION_PORT if (fd < 0 || fd >= FD_SETSIZE) { msyslog(LOG_ERR, "Too many sockets in use, FD_SETSIZE %d exceeded", FD_SETSIZE); exit(1); } /* * keep activefds in sync */ if (fd > maxactivefd) maxactivefd = fd; FD_SET( (u_int)fd, &activefds); #endif } static void close_and_delete_fd_from_list(SOCKET fd) { vsock_t *next; vsock_t *lsock = ISC_LIST_HEAD(fd_list); while(lsock != NULL) { next = ISC_LIST_NEXT(lsock, link); if(lsock->fd == fd) { ISC_LIST_DEQUEUE_TYPE(fd_list, lsock, link, vsock_t); switch (lsock->type) { case FD_TYPE_SOCKET: #ifdef SYS_WINNT closesocket(lsock->fd); break; #endif case FD_TYPE_FILE: (void) close(lsock->fd); break; default: msyslog(LOG_ERR, "internal error - illegal descriptor type %d - EXITING", (int)lsock->type); exit(1); } free(lsock); /* * I/O Completion Ports don't care about select and fd_set */ #ifndef HAVE_IO_COMPLETION_PORT /* * remove from activefds */ FD_CLR( (u_int) fd, &activefds); if (fd == maxactivefd) { int i, newmax = 0; for (i = 0; i < maxactivefd; i++) if (FD_ISSET(i, &activefds)) newmax = i; maxactivefd = newmax; } #endif break; } lsock = next; } } static void add_addr_to_list(struct sockaddr_storage *addr, struct interface *interface){ #ifdef DEBUG if (find_addr_in_list(addr) == NULL) { #endif /* not there yet - add to list */ remaddr_t *laddr = (remaddr_t *)emalloc(sizeof(remaddr_t)); memcpy(&laddr->addr, addr, sizeof(struct sockaddr_storage)); laddr->interface = interface; ISC_LIST_APPEND(remoteaddr_list, laddr, link); DPRINTF(4, ("Added addr %s to list of addresses\n", stoa(addr))); #ifdef DEBUG } else { DPRINTF(4, ("WARNING: Attempt to add duplicate addr %s to address list\n", stoa(addr))); } #endif } static void delete_addr_from_list(struct sockaddr_storage *addr) { remaddr_t *next; remaddr_t *laddr = ISC_LIST_HEAD(remoteaddr_list); while(laddr != NULL) { next = ISC_LIST_NEXT(laddr, link); if(SOCKCMP(&laddr->addr, addr)) { ISC_LIST_DEQUEUE_TYPE(remoteaddr_list, laddr, link, remaddr_t); DPRINTF(4, ("Deleted addr %s from list of addresses\n", stoa(addr))); free(laddr); break; } laddr = next; } } static void delete_interface_from_list(struct interface *iface) { remaddr_t *next; remaddr_t *laddr = ISC_LIST_HEAD(remoteaddr_list); while(laddr != NULL) { next = ISC_LIST_NEXT(laddr, link); if (laddr->interface == iface) { ISC_LIST_DEQUEUE_TYPE(remoteaddr_list, laddr, link, remaddr_t); DPRINTF(4, ("Deleted addr %s for interface #%d %s from list of addresses\n", stoa(&laddr->addr), iface->ifnum, iface->name)); free(laddr); } laddr = next; } } static struct interface * find_addr_in_list(struct sockaddr_storage *addr) { remaddr_t *next; remaddr_t *laddr = ISC_LIST_HEAD(remoteaddr_list); DPRINTF(4, ("Searching for addr %s in list of addresses - ", stoa(addr))); while(laddr != NULL) { next = ISC_LIST_NEXT(laddr, link); if(SOCKCMP(&laddr->addr, addr)) { DPRINTF(4, ("FOUND\n")); return laddr->interface; } else laddr = next; } DPRINTF(4, ("NOT FOUND\n")); return NULL; /* Not found */ } /* * Find the given address with the associated flag in the list */ static struct interface * find_flagged_addr_in_list(struct sockaddr_storage *addr, int flag) { remaddr_t *next; remaddr_t *laddr = ISC_LIST_HEAD(remoteaddr_list); DPRINTF(4, ("Finding addr %s in list of addresses\n", stoa(addr))); while(laddr != NULL) { next = ISC_LIST_NEXT(laddr, link); if(SOCKCMP(&laddr->addr, addr) && (laddr->interface->flags & flag)) { return laddr->interface; break; } else laddr = next; } return NULL; /* Not found */ } #ifdef HAS_ROUTING_SOCKET #include #ifndef UPDATE_GRACE #define UPDATE_GRACE 2 /* wait UPDATE_GRACE seconds before scanning */ #endif static void process_routing_msgs(struct asyncio_reader *reader) { char buffer[5120]; char *p = buffer; int cnt; if (disable_dynamic_updates) { /* * discard ourselves if we are not need any more * usually happens when running unprivileged */ remove_asyncio_reader(reader); delete_asyncio_reader(reader); return; } cnt = read(reader->fd, buffer, sizeof(buffer)); if (cnt < 0) { msyslog(LOG_ERR, "i/o error on routing socket %m - disabling"); remove_asyncio_reader(reader); delete_asyncio_reader(reader); return; } /* * process routing message */ while ((p + sizeof(struct rt_msghdr)) <= (buffer + cnt)) { struct rt_msghdr *rtm; rtm = (struct rt_msghdr *)p; if (rtm->rtm_version != RTM_VERSION) { msyslog(LOG_ERR, "version mismatch on routing socket %m - disabling"); remove_asyncio_reader(reader); delete_asyncio_reader(reader); return; } switch (rtm->rtm_type) { #ifdef RTM_NEWADDR case RTM_NEWADDR: #endif #ifdef RTM_DELADDR case RTM_DELADDR: #endif #ifdef RTM_ADD case RTM_ADD: #endif #ifdef RTM_DELETE case RTM_DELETE: #endif #ifdef RTM_REDIRECT case RTM_REDIRECT: #endif #ifdef RTM_CHANGE case RTM_CHANGE: #endif #ifdef RTM_LOSING case RTM_LOSING: #endif #ifdef RTM_IFINFO case RTM_IFINFO: #endif #ifdef RTM_IFANNOUNCE case RTM_IFANNOUNCE: #endif /* * we are keen on new and deleted addresses and if an interface goes up and down or routing changes */ DPRINTF(3, ("routing message op = %d: scheduling interface update\n", rtm->rtm_type)); timer_interfacetimeout(current_time + UPDATE_GRACE); break; default: /* * the rest doesn't bother us. */ DPRINTF(4, ("routing message op = %d: ignored\n", rtm->rtm_type)); break; } p += rtm->rtm_msglen; } } /* * set up routing notifications */ static void init_async_notifications() { struct asyncio_reader *reader; int fd = socket(PF_ROUTE, SOCK_RAW, 0); if (fd >= 0) { fd = move_fd(fd); init_nonblocking_io(fd); #if defined(HAVE_SIGNALED_IO) init_socket_sig(fd); #endif /* HAVE_SIGNALED_IO */ reader = new_asyncio_reader(); reader->fd = fd; reader->receiver = process_routing_msgs; add_asyncio_reader(reader, FD_TYPE_SOCKET); msyslog(LOG_INFO, "Listening on routing socket on fd #%d for interface updates", fd); } else { msyslog(LOG_ERR, "unable to open routing socket (%m) - using polled interface update"); } } #else static void init_async_notifications() { } #endif Index: projects/ifnet/contrib/ntp =================================================================== --- projects/ifnet/contrib/ntp (revision 277209) +++ projects/ifnet/contrib/ntp (revision 277210) Property changes on: projects/ifnet/contrib/ntp ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/contrib/ntp:r276838-277209 Index: projects/ifnet/sys/amd64/conf/MINIMAL =================================================================== --- projects/ifnet/sys/amd64/conf/MINIMAL (nonexistent) +++ projects/ifnet/sys/amd64/conf/MINIMAL (revision 277210) @@ -0,0 +1,145 @@ +# +# MINIMAL -- Mostly Minimal kernel configuration file for FreeBSD/amd64 +# +# Many definitions of minimal are possible. The one this file follows is +# GENERIC, minus all functionality that can be replaced by loading kernel +# modules. +# +# Exceptions: +# o While UFS is buildable as a module, the current module lacks +# some features (ACL, GJOURNAL) that GENERIC includes. +# o acpi as a module has been reported flakey and not well tested, so +# is included in the kernel. +# o random is included due to uncertaty... +# o Many networking things are included +# +# For now, please run changes to these list past imp@freebsd.org +# +# For more information on this file, please read the config(5) manual page, +# and/or the handbook section on Kernel Configuration Files: +# +# http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html +# +# The handbook is also available locally in /usr/share/doc/handbook +# if you've installed the doc distribution, otherwise always see the +# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the +# latest information. +# +# An exhaustive list of options and more detailed explanations of the +# device lines is also present in the ../../conf/NOTES and NOTES files. +# If you are in doubt as to the purpose or necessity of a line, check first +# in NOTES. +# +# $FreeBSD$ + +cpu HAMMER +ident MINIMAL + +makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols +makeoptions WITH_CTF=1 # Run ctfconvert(1) for DTrace support + +options SCHED_ULE # ULE scheduler +options PREEMPTION # Enable kernel thread preemption +options INET # InterNETworking +options INET6 # IPv6 communications protocols +options TCP_OFFLOAD # TCP offload +options SCTP # Stream Control Transmission Protocol +options FFS # Berkeley Fast Filesystem +options SOFTUPDATES # Enable FFS soft updates support +options UFS_ACL # Support for access control lists +options UFS_DIRHASH # Improve performance on big directories +options UFS_GJOURNAL # Enable gjournal-based UFS journaling +options QUOTA # Enable disk quotas for UFS +options MD_ROOT # MD is a potential root device +options COMPAT_FREEBSD32 # Compatible with i386 binaries +options COMPAT_FREEBSD4 # Compatible with FreeBSD4 +options COMPAT_FREEBSD5 # Compatible with FreeBSD5 +options COMPAT_FREEBSD6 # Compatible with FreeBSD6 +options COMPAT_FREEBSD7 # Compatible with FreeBSD7 +options COMPAT_FREEBSD9 # Compatible with FreeBSD9 +options COMPAT_FREEBSD10 # Compatible with FreeBSD10 +options SCSI_DELAY=5000 # Delay (in ms) before probing SCSI +options KTRACE # ktrace(1) support +options STACK # stack(9) support +options SYSVSHM # SYSV-style shared memory +options SYSVMSG # SYSV-style message queues +options SYSVSEM # SYSV-style semaphores +options _KPOSIX_PRIORITY_SCHEDULING # POSIX P1003_1B real-time extensions +options PRINTF_BUFR_SIZE=128 # Prevent printf output being interspersed. +options KBD_INSTALL_CDEV # install a CDEV entry in /dev +options HWPMC_HOOKS # Necessary kernel hooks for hwpmc(4) +options AUDIT # Security event auditing +options CAPABILITY_MODE # Capsicum capability mode +options CAPABILITIES # Capsicum capabilities +options MAC # TrustedBSD MAC Framework +options KDTRACE_FRAME # Ensure frames are compiled in +options KDTRACE_HOOKS # Kernel DTrace hooks +options DDB_CTF # Kernel ELF linker loads CTF data +options INCLUDE_CONFIG_FILE # Include this file in kernel + +# Debugging support. Always need this: +options KDB # Enable kernel debugger support. +options KDB_TRACE # Print a stack trace for a panic. +# For full debugger support use (turn off in stable branch): +options DDB # Support DDB. +options GDB # Support remote GDB. +options DEADLKRES # Enable the deadlock resolver +options INVARIANTS # Enable calls of extra sanity checking +options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS +options WITNESS # Enable checks to detect deadlocks and cycles +options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed +options MALLOC_DEBUG_MAXZONES=8 # Separate malloc(9) zones + +# Make an SMP-capable kernel by default +options SMP # Symmetric MultiProcessor Kernel + +# CPU frequency control +device cpufreq + +# Bus support. +device acpi +options ACPI_DMAR +device pci + +# atkbdc0 controls both the keyboard and the PS/2 mouse +device atkbdc # AT keyboard controller +device atkbd # AT keyboard +device psm # PS/2 mouse + +device kbdmux # keyboard multiplexer + +device vga # VGA video card driver +options VESA # Add support for VESA BIOS Extensions (VBE) + +device splash # Splash screen and screen saver support + +# syscons is the default console driver, resembling an SCO console +device sc +options SC_PIXEL_MODE # add support for the raster text mode + +# vt is the new video console driver +device vt +device vt_vga +device vt_efifb + +device agp # support several AGP chipsets + +# Pseudo devices. +device loop # Network loopback +device random # Entropy device +device padlock_rng # VIA Padlock RNG +device rdrand_rng # Intel Bull Mountain RNG +device ether # Ethernet support +device vlan # 802.1Q VLAN support +device tun # Packet tunnel. +device gif # IPv6 and IPv4 tunneling + +# The `bpf' device enables the Berkeley Packet Filter. +# Be aware of the administrative consequences of enabling this! +# Note that 'bpf' is required for DHCP. +device bpf # Berkeley packet filter + +# Xen HVM Guest Optimizations +# NOTE: XENHVM depends on xenpci. They must be added or removed together. +options XENHVM # Xen HVM kernel infrastructure +device xenpci # Xen HVM Hypervisor services driver Property changes on: projects/ifnet/sys/amd64/conf/MINIMAL ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/ifnet/sys/arm/broadcom/bcm2835/bcm2835_bsc.c =================================================================== --- projects/ifnet/sys/arm/broadcom/bcm2835/bcm2835_bsc.c (revision 277209) +++ projects/ifnet/sys/arm/broadcom/bcm2835/bcm2835_bsc.c (revision 277210) @@ -1,505 +1,507 @@ /*- * Copyright (c) 2001 Tsubai Masanari. * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2013 Luiz Otavio O Souza * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" static void bcm_bsc_intr(void *); static int bcm_bsc_detach(device_t); static void bcm_bsc_modifyreg(struct bcm_bsc_softc *sc, uint32_t off, uint32_t mask, uint32_t value) { uint32_t reg; mtx_assert(&sc->sc_mtx, MA_OWNED); reg = BCM_BSC_READ(sc, off); reg &= ~mask; reg |= value; BCM_BSC_WRITE(sc, off, reg); } static int bcm_bsc_clock_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clk; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK); BCM_BSC_UNLOCK(sc); clk &= 0xffff; if (clk == 0) clk = 32768; clk = BCM_BSC_CORE_CLK / clk; return (sysctl_handle_int(oidp, &clk, 0, req)); } static int bcm_bsc_clkt_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clkt; int error; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); clkt = BCM_BSC_READ(sc, BCM_BSC_CLKT); BCM_BSC_UNLOCK(sc); clkt &= 0xffff; error = sysctl_handle_int(oidp, &clkt, sizeof(clkt), req); if (error != 0 || req->newptr == NULL) return (error); BCM_BSC_LOCK(sc); BCM_BSC_WRITE(sc, BCM_BSC_CLKT, clkt & 0xffff); BCM_BSC_UNLOCK(sc); return (0); } static int bcm_bsc_fall_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clk, reg; int error; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); reg = BCM_BSC_READ(sc, BCM_BSC_DELAY); BCM_BSC_UNLOCK(sc); reg >>= 16; error = sysctl_handle_int(oidp, ®, sizeof(reg), req); if (error != 0 || req->newptr == NULL) return (error); BCM_BSC_LOCK(sc); clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK); clk = BCM_BSC_CORE_CLK / clk; if (reg > clk / 2) reg = clk / 2 - 1; bcm_bsc_modifyreg(sc, BCM_BSC_DELAY, 0xffff0000, reg << 16); BCM_BSC_UNLOCK(sc); return (0); } static int bcm_bsc_rise_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clk, reg; int error; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); reg = BCM_BSC_READ(sc, BCM_BSC_DELAY); BCM_BSC_UNLOCK(sc); reg &= 0xffff; error = sysctl_handle_int(oidp, ®, sizeof(reg), req); if (error != 0 || req->newptr == NULL) return (error); BCM_BSC_LOCK(sc); clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK); clk = BCM_BSC_CORE_CLK / clk; if (reg > clk / 2) reg = clk / 2 - 1; bcm_bsc_modifyreg(sc, BCM_BSC_DELAY, 0xffff, reg); BCM_BSC_UNLOCK(sc); return (0); } static void bcm_bsc_sysctl_init(struct bcm_bsc_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid *tree_node; struct sysctl_oid_list *tree; /* * Add system sysctl tree/handlers. */ ctx = device_get_sysctl_ctx(sc->sc_dev); tree_node = device_get_sysctl_tree(sc->sc_dev); tree = SYSCTL_CHILDREN(tree_node); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "frequency", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_bsc_clock_proc, "IU", "I2C BUS clock frequency"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "clock_stretch", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_bsc_clkt_proc, "IU", "I2C BUS clock stretch timeout"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "fall_edge_delay", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_bsc_fall_proc, "IU", "I2C BUS falling edge delay"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "rise_edge_delay", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_bsc_rise_proc, "IU", "I2C BUS rising edge delay"); } static void bcm_bsc_reset(struct bcm_bsc_softc *sc) { /* Enable the BSC Controller, disable interrupts. */ BCM_BSC_WRITE(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_I2CEN); /* Clear pending interrupts. */ BCM_BSC_WRITE(sc, BCM_BSC_STATUS, BCM_BSC_STATUS_CLKT | BCM_BSC_STATUS_ERR | BCM_BSC_STATUS_DONE); /* Clear the FIFO. */ bcm_bsc_modifyreg(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_CLEAR0, BCM_BSC_CTRL_CLEAR0); } static int bcm_bsc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-bsc")) return (ENXIO); device_set_desc(dev, "BCM2708/2835 BSC controller"); return (BUS_PROBE_DEFAULT); } static int bcm_bsc_attach(device_t dev) { struct bcm_bsc_softc *sc; unsigned long start; device_t gpio; int i, rid; sc = device_get_softc(dev); sc->sc_dev = dev; rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); return (ENXIO); } sc->sc_bst = rman_get_bustag(sc->sc_mem_res); sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); /* Check the unit we are attaching by its base address. */ start = rman_get_start(sc->sc_mem_res); for (i = 0; i < nitems(bcm_bsc_pins); i++) { if (bcm_bsc_pins[i].start == start) break; } if (i == nitems(bcm_bsc_pins)) { device_printf(dev, "only bsc0 and bsc1 are supported\n"); + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (ENXIO); } /* * Configure the GPIO pins to ALT0 function to enable BSC control * over the pins. */ gpio = devclass_get_device(devclass_find("gpio"), 0); if (!gpio) { device_printf(dev, "cannot find gpio0\n"); + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (ENXIO); } bcm_gpio_set_alternate(gpio, bcm_bsc_pins[i].sda, BCM_GPIO_ALT0); bcm_gpio_set_alternate(gpio, bcm_bsc_pins[i].scl, BCM_GPIO_ALT0); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (!sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot allocate interrupt\n"); return (ENXIO); } /* Hook up our interrupt handler. */ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, bcm_bsc_intr, sc, &sc->sc_intrhand)) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot setup the interrupt handler\n"); return (ENXIO); } mtx_init(&sc->sc_mtx, "bcm_bsc", NULL, MTX_DEF); bcm_bsc_sysctl_init(sc); /* Enable the BSC controller. Flush the FIFO. */ BCM_BSC_LOCK(sc); bcm_bsc_reset(sc); BCM_BSC_UNLOCK(sc); sc->sc_iicbus = device_add_child(dev, "iicbus", -1); if (sc->sc_iicbus == NULL) { bcm_bsc_detach(dev); return (ENXIO); } return (bus_generic_attach(dev)); } static int bcm_bsc_detach(device_t dev) { struct bcm_bsc_softc *sc; bus_generic_detach(dev); sc = device_get_softc(dev); mtx_destroy(&sc->sc_mtx); if (sc->sc_intrhand) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (0); } static void bcm_bsc_intr(void *arg) { struct bcm_bsc_softc *sc; uint32_t status; sc = (struct bcm_bsc_softc *)arg; BCM_BSC_LOCK(sc); /* The I2C interrupt is shared among all the BSC controllers. */ if ((sc->sc_flags & BCM_I2C_BUSY) == 0) { BCM_BSC_UNLOCK(sc); return; } status = BCM_BSC_READ(sc, BCM_BSC_STATUS); /* Check for errors. */ if (status & (BCM_BSC_STATUS_CLKT | BCM_BSC_STATUS_ERR)) { /* Disable interrupts. */ bcm_bsc_reset(sc); sc->sc_flags |= BCM_I2C_ERROR; wakeup(sc->sc_dev); BCM_BSC_UNLOCK(sc); return; } if (sc->sc_flags & BCM_I2C_READ) { while (sc->sc_resid > 0 && (status & BCM_BSC_STATUS_RXD)) { *sc->sc_data++ = BCM_BSC_READ(sc, BCM_BSC_DATA); sc->sc_resid--; status = BCM_BSC_READ(sc, BCM_BSC_STATUS); } } else { while (sc->sc_resid > 0 && (status & BCM_BSC_STATUS_TXD)) { BCM_BSC_WRITE(sc, BCM_BSC_DATA, *sc->sc_data++); sc->sc_resid--; status = BCM_BSC_READ(sc, BCM_BSC_STATUS); } } if (status & BCM_BSC_STATUS_DONE) { /* Disable interrupts. */ bcm_bsc_reset(sc); wakeup(sc->sc_dev); } BCM_BSC_UNLOCK(sc); } static int bcm_bsc_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { struct bcm_bsc_softc *sc; uint32_t intr, read, status; int i, err; sc = device_get_softc(dev); BCM_BSC_LOCK(sc); /* If the controller is busy wait until it is available. */ while (sc->sc_flags & BCM_I2C_BUSY) mtx_sleep(dev, &sc->sc_mtx, 0, "bscbusw", 0); /* Now we have control over the BSC controller. */ sc->sc_flags = BCM_I2C_BUSY; /* Clear the FIFO and the pending interrupts. */ bcm_bsc_reset(sc); err = 0; for (i = 0; i < nmsgs; i++) { /* Write the slave address. */ BCM_BSC_WRITE(sc, BCM_BSC_SLAVE, msgs[i].slave >> 1); /* Write the data length. */ BCM_BSC_WRITE(sc, BCM_BSC_DLEN, msgs[i].len); sc->sc_data = msgs[i].buf; sc->sc_resid = msgs[i].len; if ((msgs[i].flags & IIC_M_RD) == 0) { /* Fill up the TX FIFO. */ status = BCM_BSC_READ(sc, BCM_BSC_STATUS); while (sc->sc_resid > 0 && (status & BCM_BSC_STATUS_TXD)) { BCM_BSC_WRITE(sc, BCM_BSC_DATA, *sc->sc_data); sc->sc_data++; sc->sc_resid--; status = BCM_BSC_READ(sc, BCM_BSC_STATUS); } read = 0; intr = BCM_BSC_CTRL_INTT; sc->sc_flags &= ~BCM_I2C_READ; } else { sc->sc_flags |= BCM_I2C_READ; read = BCM_BSC_CTRL_READ; intr = BCM_BSC_CTRL_INTR; } intr |= BCM_BSC_CTRL_INTD; /* Start the transfer. */ BCM_BSC_WRITE(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_I2CEN | BCM_BSC_CTRL_ST | read | intr); /* Wait for the transaction to complete. */ err = mtx_sleep(dev, &sc->sc_mtx, 0, "bsciow", hz); /* Check for errors. */ if (err == 0 && (sc->sc_flags & BCM_I2C_ERROR)) err = EIO; if (err != 0) break; } /* Clean the controller flags. */ sc->sc_flags = 0; /* Wake up the threads waiting for bus. */ wakeup(dev); BCM_BSC_UNLOCK(sc); return (err); } static int bcm_bsc_iicbus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { struct bcm_bsc_softc *sc; uint32_t busfreq; sc = device_get_softc(dev); BCM_BSC_LOCK(sc); bcm_bsc_reset(sc); if (sc->sc_iicbus == NULL) busfreq = 100000; else busfreq = IICBUS_GET_FREQUENCY(sc->sc_iicbus, speed); BCM_BSC_WRITE(sc, BCM_BSC_CLOCK, BCM_BSC_CORE_CLK / busfreq); BCM_BSC_UNLOCK(sc); return (IIC_ENOADDR); } static phandle_t bcm_bsc_get_node(device_t bus, device_t dev) { /* We only have one child, the I2C bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } static device_method_t bcm_bsc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_bsc_probe), DEVMETHOD(device_attach, bcm_bsc_attach), DEVMETHOD(device_detach, bcm_bsc_detach), /* iicbus interface */ DEVMETHOD(iicbus_reset, bcm_bsc_iicbus_reset), DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_transfer, bcm_bsc_transfer), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, bcm_bsc_get_node), DEVMETHOD_END }; static devclass_t bcm_bsc_devclass; static driver_t bcm_bsc_driver = { "iichb", bcm_bsc_methods, sizeof(struct bcm_bsc_softc), }; DRIVER_MODULE(iicbus, bcm2835_bsc, iicbus_driver, iicbus_devclass, 0, 0); DRIVER_MODULE(bcm2835_bsc, simplebus, bcm_bsc_driver, bcm_bsc_devclass, 0, 0); Index: projects/ifnet/sys/arm/broadcom/bcm2835/bcm2835_bscvar.h =================================================================== --- projects/ifnet/sys/arm/broadcom/bcm2835/bcm2835_bscvar.h (revision 277209) +++ projects/ifnet/sys/arm/broadcom/bcm2835/bcm2835_bscvar.h (revision 277210) @@ -1,70 +1,70 @@ /*- * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2013 Luiz Otavio O Souza * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _BCM2835_BSCVAR_H #define _BCM2835_BSCVAR_H struct { uint32_t sda; uint32_t scl; unsigned long start; } bcm_bsc_pins[] = { { 0, 1, 0x20205000 }, /* BSC0 GPIO pins and base address. */ { 2, 3, 0x20804000 } /* BSC1 GPIO pins and base address. */ }; struct bcm_bsc_softc { device_t sc_dev; device_t sc_iicbus; struct mtx sc_mtx; struct resource * sc_mem_res; struct resource * sc_irq_res; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; uint16_t sc_resid; uint8_t *sc_data; uint8_t sc_flags; void * sc_intrhand; }; #define BCM_I2C_BUSY 0x01 #define BCM_I2C_READ 0x02 #define BCM_I2C_ERROR 0x04 #define BCM_BSC_WRITE(_sc, _off, _val) \ - bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val) + bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val) #define BCM_BSC_READ(_sc, _off) \ - bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off) + bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off) #define BCM_BSC_LOCK(_sc) \ mtx_lock(&(_sc)->sc_mtx) #define BCM_BSC_UNLOCK(_sc) \ mtx_unlock(&(_sc)->sc_mtx) #endif /* _BCM2835_BSCVAR_H_ */ Index: projects/ifnet/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c =================================================================== --- projects/ifnet/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c (revision 277209) +++ projects/ifnet/sys/arm/broadcom/bcm2835/bcm2835_cpufreq.c (revision 277210) @@ -1,1818 +1,1817 @@ /*- * Copyright (C) 2013-2014 Daisuke Aoyama * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cpufreq_if.h" #include "mbox_if.h" #ifdef DEBUG #define DPRINTF(fmt, ...) do { \ printf("%s:%u: ", __func__, __LINE__); \ printf(fmt, ##__VA_ARGS__); \ } while (0) #else #define DPRINTF(fmt, ...) #endif #define HZ2MHZ(freq) ((freq) / (1000 * 1000)) #define MHZ2HZ(freq) ((freq) * (1000 * 1000)) #define OFFSET2MVOLT(val) (1200 + ((val) * 25)) #define MVOLT2OFFSET(val) (((val) - 1200) / 25) -#define RAW2K(temp) (((temp) + 273150) / 1000) -#define K2RAW(temp) (((temp) * 1000) - 273150) #define DEFAULT_ARM_FREQUENCY 700 #define DEFAULT_CORE_FREQUENCY 250 #define DEFAULT_SDRAM_FREQUENCY 400 #define DEFAULT_LOWEST_FREQ 300 #define TRANSITION_LATENCY 1000 #define MIN_OVER_VOLTAGE -16 #define MAX_OVER_VOLTAGE 6 #define MSG_ERROR -999999999 #define MHZSTEP 100 #define HZSTEP (MHZ2HZ(MHZSTEP)) +#define TZ_ZEROC 2732 #define VC_LOCK(sc) do { \ sema_wait(&vc_sema); \ } while (0) #define VC_UNLOCK(sc) do { \ sema_post(&vc_sema); \ } while (0) /* ARM->VC mailbox property semaphore */ static struct sema vc_sema; static struct sysctl_ctx_list bcm2835_sysctl_ctx; struct bcm2835_cpufreq_softc { device_t dev; int arm_max_freq; int arm_min_freq; int core_max_freq; int core_min_freq; int sdram_max_freq; int sdram_min_freq; int max_voltage_core; int min_voltage_core; /* the values written in mbox */ int voltage_core; int voltage_sdram; int voltage_sdram_c; int voltage_sdram_i; int voltage_sdram_p; int turbo_mode; /* mbox buffer (physical address) */ bus_dma_tag_t dma_tag; bus_dmamap_t dma_map; bus_size_t dma_size; void *dma_buf; bus_addr_t dma_phys; /* initial hook for waiting mbox intr */ struct intr_config_hook init_hook; }; static int cpufreq_verbose = 0; TUNABLE_INT("hw.bcm2835.cpufreq.verbose", &cpufreq_verbose); static int cpufreq_lowest_freq = DEFAULT_LOWEST_FREQ; TUNABLE_INT("hw.bcm2835.cpufreq.lowest_freq", &cpufreq_lowest_freq); #ifdef PROP_DEBUG static void bcm2835_dump(const void *data, int len) { const uint8_t *p = (const uint8_t*)data; int i; printf("dump @ %p:\n", data); for (i = 0; i < len; i++) { printf("%2.2x ", p[i]); if ((i % 4) == 3) printf(" "); if ((i % 16) == 15) printf("\n"); } printf("\n"); } #endif static int bcm2835_mbox_call_prop(struct bcm2835_cpufreq_softc *sc) { struct bcm2835_mbox_hdr *msg = (struct bcm2835_mbox_hdr *)sc->dma_buf; struct bcm2835_mbox_tag_hdr *tag, *last; uint8_t *up; device_t mbox; size_t hdr_size; int idx; int err; /* * For multiple calls, locking is not here. The caller must have * VC semaphore. */ /* get mbox device */ mbox = devclass_get_device(devclass_find("mbox"), 0); if (mbox == NULL) { device_printf(sc->dev, "can't find mbox\n"); return (-1); } /* go mailbox property */ #ifdef PROP_DEBUG bcm2835_dump(msg, 64); #endif bus_dmamap_sync(sc->dma_tag, sc->dma_map, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); MBOX_WRITE(mbox, BCM2835_MBOX_CHAN_PROP, (uint32_t)sc->dma_phys); MBOX_READ(mbox, BCM2835_MBOX_CHAN_PROP, &err); bus_dmamap_sync(sc->dma_tag, sc->dma_map, BUS_DMASYNC_POSTREAD); #ifdef PROP_DEBUG bcm2835_dump(msg, 64); #endif /* check response code */ if (msg->code != BCM2835_MBOX_CODE_RESP_SUCCESS) { device_printf(sc->dev, "mbox response error\n"); return (-1); } /* tag = first tag */ up = (uint8_t *)msg; hdr_size = sizeof(struct bcm2835_mbox_hdr); tag = (struct bcm2835_mbox_tag_hdr *)(up + hdr_size); /* last = end of buffer specified by header */ last = (struct bcm2835_mbox_tag_hdr *)(up + msg->buf_size); /* loop unitl end tag (=0x0) */ hdr_size = sizeof(struct bcm2835_mbox_tag_hdr); for (idx = 0; tag->tag != 0; idx++) { if ((tag->val_len & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE) == 0) { device_printf(sc->dev, "tag%d response error\n", idx); return (-1); } /* clear response bit */ tag->val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; /* get next tag */ up = (uint8_t *)tag; tag = (struct bcm2835_mbox_tag_hdr *)(up + hdr_size + tag->val_buf_size); /* check buffer size of header */ if (tag > last) { device_printf(sc->dev, "mbox buffer size error\n"); return (-1); } } return (0); } static int bcm2835_cpufreq_get_clock_rate(struct bcm2835_cpufreq_softc *sc, uint32_t clock_id) { struct msg_get_clock_rate *msg; int rate; int err; /* * Get clock rate * Tag: 0x00030002 * Request: * Length: 4 * Value: * u32: clock id * Response: * Length: 8 * Value: * u32: clock id * u32: rate (in Hz) */ /* using DMA buffer for VC */ msg = (struct msg_get_clock_rate *)sc->dma_buf; if (sizeof(*msg) > sc->dma_size) { device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", sizeof(*msg), sc->dma_size); return (MSG_ERROR); } /* setup single tag buffer */ memset(msg, 0, sizeof(*msg)); msg->hdr.buf_size = sizeof(*msg); msg->hdr.code = BCM2835_MBOX_CODE_REQ; msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_CLOCK_RATE; msg->tag_hdr.val_buf_size = sizeof(msg->body); msg->tag_hdr.val_len = sizeof(msg->body.req); msg->body.req.clock_id = clock_id; msg->end_tag = 0; /* call mailbox property */ err = bcm2835_mbox_call_prop(sc); if (err) { device_printf(sc->dev, "can't get clock rate (id=%u)\n", clock_id); return (MSG_ERROR); } /* result (Hz) */ rate = (int)msg->body.resp.rate_hz; DPRINTF("clock = %d(Hz)\n", rate); return (rate); } static int bcm2835_cpufreq_get_max_clock_rate(struct bcm2835_cpufreq_softc *sc, uint32_t clock_id) { struct msg_get_max_clock_rate *msg; int rate; int err; /* * Get max clock rate * Tag: 0x00030004 * Request: * Length: 4 * Value: * u32: clock id * Response: * Length: 8 * Value: * u32: clock id * u32: rate (in Hz) */ /* using DMA buffer for VC */ msg = (struct msg_get_max_clock_rate *)sc->dma_buf; if (sizeof(*msg) > sc->dma_size) { device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", sizeof(*msg), sc->dma_size); return (MSG_ERROR); } /* setup single tag buffer */ memset(msg, 0, sizeof(*msg)); msg->hdr.buf_size = sizeof(*msg); msg->hdr.code = BCM2835_MBOX_CODE_REQ; msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_MAX_CLOCK_RATE; msg->tag_hdr.val_buf_size = sizeof(msg->body); msg->tag_hdr.val_len = sizeof(msg->body.req); msg->body.req.clock_id = clock_id; msg->end_tag = 0; /* call mailbox property */ err = bcm2835_mbox_call_prop(sc); if (err) { device_printf(sc->dev, "can't get max clock rate (id=%u)\n", clock_id); return (MSG_ERROR); } /* result (Hz) */ rate = (int)msg->body.resp.rate_hz; DPRINTF("clock = %d(Hz)\n", rate); return (rate); } static int bcm2835_cpufreq_get_min_clock_rate(struct bcm2835_cpufreq_softc *sc, uint32_t clock_id) { struct msg_get_min_clock_rate *msg; int rate; int err; /* * Get min clock rate * Tag: 0x00030007 * Request: * Length: 4 * Value: * u32: clock id * Response: * Length: 8 * Value: * u32: clock id * u32: rate (in Hz) */ /* using DMA buffer for VC */ msg = (struct msg_get_min_clock_rate *)sc->dma_buf; if (sizeof(*msg) > sc->dma_size) { device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", sizeof(*msg), sc->dma_size); return (MSG_ERROR); } /* setup single tag buffer */ memset(msg, 0, sizeof(*msg)); msg->hdr.buf_size = sizeof(*msg); msg->hdr.code = BCM2835_MBOX_CODE_REQ; msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_MIN_CLOCK_RATE; msg->tag_hdr.val_buf_size = sizeof(msg->body); msg->tag_hdr.val_len = sizeof(msg->body.req); msg->body.req.clock_id = clock_id; msg->end_tag = 0; /* call mailbox property */ err = bcm2835_mbox_call_prop(sc); if (err) { device_printf(sc->dev, "can't get min clock rate (id=%u)\n", clock_id); return (MSG_ERROR); } /* result (Hz) */ rate = (int)msg->body.resp.rate_hz; DPRINTF("clock = %d(Hz)\n", rate); return (rate); } static int bcm2835_cpufreq_set_clock_rate(struct bcm2835_cpufreq_softc *sc, uint32_t clock_id, uint32_t rate_hz) { struct msg_set_clock_rate *msg; int rate; int err; /* * Set clock rate * Tag: 0x00038002 * Request: * Length: 8 * Value: * u32: clock id * u32: rate (in Hz) * Response: * Length: 8 * Value: * u32: clock id * u32: rate (in Hz) */ /* using DMA buffer for VC */ msg = (struct msg_set_clock_rate *)sc->dma_buf; if (sizeof(*msg) > sc->dma_size) { device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", sizeof(*msg), sc->dma_size); return (MSG_ERROR); } /* setup single tag buffer */ memset(msg, 0, sizeof(*msg)); msg->hdr.buf_size = sizeof(*msg); msg->hdr.code = BCM2835_MBOX_CODE_REQ; msg->tag_hdr.tag = BCM2835_MBOX_TAG_SET_CLOCK_RATE; msg->tag_hdr.val_buf_size = sizeof(msg->body); msg->tag_hdr.val_len = sizeof(msg->body.req); msg->body.req.clock_id = clock_id; msg->body.req.rate_hz = rate_hz; msg->end_tag = 0; /* call mailbox property */ err = bcm2835_mbox_call_prop(sc); if (err) { device_printf(sc->dev, "can't set clock rate (id=%u)\n", clock_id); return (MSG_ERROR); } /* workaround for core clock */ if (clock_id == BCM2835_MBOX_CLOCK_ID_CORE) { /* for safety (may change voltage without changing clock) */ DELAY(TRANSITION_LATENCY); /* * XXX: the core clock is unable to change at once, * to change certainly, write it twice now. */ /* setup single tag buffer */ memset(msg, 0, sizeof(*msg)); msg->hdr.buf_size = sizeof(*msg); msg->hdr.code = BCM2835_MBOX_CODE_REQ; msg->tag_hdr.tag = BCM2835_MBOX_TAG_SET_CLOCK_RATE; msg->tag_hdr.val_buf_size = sizeof(msg->body); msg->tag_hdr.val_len = sizeof(msg->body.req); msg->body.req.clock_id = clock_id; msg->body.req.rate_hz = rate_hz; msg->end_tag = 0; /* call mailbox property */ err = bcm2835_mbox_call_prop(sc); if (err) { device_printf(sc->dev, "can't set clock rate (id=%u)\n", clock_id); return (MSG_ERROR); } } /* result (Hz) */ rate = (int)msg->body.resp.rate_hz; DPRINTF("clock = %d(Hz)\n", rate); return (rate); } static int bcm2835_cpufreq_get_turbo(struct bcm2835_cpufreq_softc *sc) { struct msg_get_turbo *msg; int level; int err; /* * Get turbo * Tag: 0x00030009 * Request: * Length: 4 * Value: * u32: id * Response: * Length: 8 * Value: * u32: id * u32: level */ /* using DMA buffer for VC */ msg = (struct msg_get_turbo *)sc->dma_buf; if (sizeof(*msg) > sc->dma_size) { device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", sizeof(*msg), sc->dma_size); return (MSG_ERROR); } /* setup single tag buffer */ memset(msg, 0, sizeof(*msg)); msg->hdr.buf_size = sizeof(*msg); msg->hdr.code = BCM2835_MBOX_CODE_REQ; msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_TURBO; msg->tag_hdr.val_buf_size = sizeof(msg->body); msg->tag_hdr.val_len = sizeof(msg->body.req); msg->body.req.id = 0; msg->end_tag = 0; /* call mailbox property */ err = bcm2835_mbox_call_prop(sc); if (err) { device_printf(sc->dev, "can't get turbo\n"); return (MSG_ERROR); } /* result 0=non-turbo, 1=turbo */ level = (int)msg->body.resp.level; DPRINTF("level = %d\n", level); return (level); } static int bcm2835_cpufreq_set_turbo(struct bcm2835_cpufreq_softc *sc, uint32_t level) { struct msg_set_turbo *msg; int value; int err; /* * Set turbo * Tag: 0x00038009 * Request: * Length: 8 * Value: * u32: id * u32: level * Response: * Length: 8 * Value: * u32: id * u32: level */ /* using DMA buffer for VC */ msg = (struct msg_set_turbo *)sc->dma_buf; if (sizeof(*msg) > sc->dma_size) { device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", sizeof(*msg), sc->dma_size); return (MSG_ERROR); } /* replace unknown value to OFF */ if (level != BCM2835_MBOX_TURBO_ON && level != BCM2835_MBOX_TURBO_OFF) level = BCM2835_MBOX_TURBO_OFF; /* setup single tag buffer */ memset(msg, 0, sizeof(*msg)); msg->hdr.buf_size = sizeof(*msg); msg->hdr.code = BCM2835_MBOX_CODE_REQ; msg->tag_hdr.tag = BCM2835_MBOX_TAG_SET_TURBO; msg->tag_hdr.val_buf_size = sizeof(msg->body); msg->tag_hdr.val_len = sizeof(msg->body.req); msg->body.req.id = 0; msg->body.req.level = level; msg->end_tag = 0; /* call mailbox property */ err = bcm2835_mbox_call_prop(sc); if (err) { device_printf(sc->dev, "can't set turbo\n"); return (MSG_ERROR); } /* result 0=non-turbo, 1=turbo */ value = (int)msg->body.resp.level; DPRINTF("level = %d\n", value); return (value); } static int bcm2835_cpufreq_get_voltage(struct bcm2835_cpufreq_softc *sc, uint32_t voltage_id) { struct msg_get_voltage *msg; int value; int err; /* * Get voltage * Tag: 0x00030003 * Request: * Length: 4 * Value: * u32: voltage id * Response: * Length: 8 * Value: * u32: voltage id * u32: value (offset from 1.2V in units of 0.025V) */ /* using DMA buffer for VC */ msg = (struct msg_get_voltage *)sc->dma_buf; if (sizeof(*msg) > sc->dma_size) { device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", sizeof(*msg), sc->dma_size); return (MSG_ERROR); } /* setup single tag buffer */ memset(msg, 0, sizeof(*msg)); msg->hdr.buf_size = sizeof(*msg); msg->hdr.code = BCM2835_MBOX_CODE_REQ; msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_VOLTAGE; msg->tag_hdr.val_buf_size = sizeof(msg->body); msg->tag_hdr.val_len = sizeof(msg->body.req); msg->body.req.voltage_id = voltage_id; msg->end_tag = 0; /* call mailbox property */ err = bcm2835_mbox_call_prop(sc); if (err) { device_printf(sc->dev, "can't get voltage\n"); return (MSG_ERROR); } /* result (offset from 1.2V) */ value = (int)msg->body.resp.value; DPRINTF("value = %d\n", value); return (value); } static int bcm2835_cpufreq_get_max_voltage(struct bcm2835_cpufreq_softc *sc, uint32_t voltage_id) { struct msg_get_max_voltage *msg; int value; int err; /* * Get voltage * Tag: 0x00030005 * Request: * Length: 4 * Value: * u32: voltage id * Response: * Length: 8 * Value: * u32: voltage id * u32: value (offset from 1.2V in units of 0.025V) */ /* using DMA buffer for VC */ msg = (struct msg_get_max_voltage *)sc->dma_buf; if (sizeof(*msg) > sc->dma_size) { device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", sizeof(*msg), sc->dma_size); return (MSG_ERROR); } /* setup single tag buffer */ memset(msg, 0, sizeof(*msg)); msg->hdr.buf_size = sizeof(*msg); msg->hdr.code = BCM2835_MBOX_CODE_REQ; msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_MAX_VOLTAGE; msg->tag_hdr.val_buf_size = sizeof(msg->body); msg->tag_hdr.val_len = sizeof(msg->body.req); msg->body.req.voltage_id = voltage_id; msg->end_tag = 0; /* call mailbox property */ err = bcm2835_mbox_call_prop(sc); if (err) { device_printf(sc->dev, "can't get max voltage\n"); return (MSG_ERROR); } /* result (offset from 1.2V) */ value = (int)msg->body.resp.value; DPRINTF("value = %d\n", value); return (value); } static int bcm2835_cpufreq_get_min_voltage(struct bcm2835_cpufreq_softc *sc, uint32_t voltage_id) { struct msg_get_min_voltage *msg; int value; int err; /* * Get voltage * Tag: 0x00030008 * Request: * Length: 4 * Value: * u32: voltage id * Response: * Length: 8 * Value: * u32: voltage id * u32: value (offset from 1.2V in units of 0.025V) */ /* using DMA buffer for VC */ msg = (struct msg_get_min_voltage *)sc->dma_buf; if (sizeof(*msg) > sc->dma_size) { device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", sizeof(*msg), sc->dma_size); return (MSG_ERROR); } /* setup single tag buffer */ memset(msg, 0, sizeof(*msg)); msg->hdr.buf_size = sizeof(*msg); msg->hdr.code = BCM2835_MBOX_CODE_REQ; msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_MIN_VOLTAGE; msg->tag_hdr.val_buf_size = sizeof(msg->body); msg->tag_hdr.val_len = sizeof(msg->body.req); msg->body.req.voltage_id = voltage_id; msg->end_tag = 0; /* call mailbox property */ err = bcm2835_mbox_call_prop(sc); if (err) { device_printf(sc->dev, "can't get min voltage\n"); return (MSG_ERROR); } /* result (offset from 1.2V) */ value = (int)msg->body.resp.value; DPRINTF("value = %d\n", value); return (value); } static int bcm2835_cpufreq_set_voltage(struct bcm2835_cpufreq_softc *sc, uint32_t voltage_id, int32_t value) { struct msg_set_voltage *msg; int err; /* * Set voltage * Tag: 0x00038003 * Request: * Length: 4 * Value: * u32: voltage id * u32: value (offset from 1.2V in units of 0.025V) * Response: * Length: 8 * Value: * u32: voltage id * u32: value (offset from 1.2V in units of 0.025V) */ /* * over_voltage: * 0 (1.2 V). Values above 6 are only allowed when force_turbo or * current_limit_override are specified (which set the warranty bit). */ if (value > MAX_OVER_VOLTAGE || value < MIN_OVER_VOLTAGE) { /* currently not supported */ device_printf(sc->dev, "not supported voltage: %d\n", value); return (MSG_ERROR); } /* using DMA buffer for VC */ msg = (struct msg_set_voltage *)sc->dma_buf; if (sizeof(*msg) > sc->dma_size) { device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", sizeof(*msg), sc->dma_size); return (MSG_ERROR); } /* setup single tag buffer */ memset(msg, 0, sizeof(*msg)); msg->hdr.buf_size = sizeof(*msg); msg->hdr.code = BCM2835_MBOX_CODE_REQ; msg->tag_hdr.tag = BCM2835_MBOX_TAG_SET_VOLTAGE; msg->tag_hdr.val_buf_size = sizeof(msg->body); msg->tag_hdr.val_len = sizeof(msg->body.req); msg->body.req.voltage_id = voltage_id; msg->body.req.value = (uint32_t)value; msg->end_tag = 0; /* call mailbox property */ err = bcm2835_mbox_call_prop(sc); if (err) { device_printf(sc->dev, "can't set voltage\n"); return (MSG_ERROR); } /* result (offset from 1.2V) */ value = (int)msg->body.resp.value; DPRINTF("value = %d\n", value); return (value); } static int bcm2835_cpufreq_get_temperature(struct bcm2835_cpufreq_softc *sc) { struct msg_get_temperature *msg; int value; int err; /* * Get temperature * Tag: 0x00030006 * Request: * Length: 4 * Value: * u32: temperature id * Response: * Length: 8 * Value: * u32: temperature id * u32: value */ /* using DMA buffer for VC */ msg = (struct msg_get_temperature *)sc->dma_buf; if (sizeof(*msg) > sc->dma_size) { device_printf(sc->dev, "DMA size overflow (%zu>%lu)\n", sizeof(*msg), sc->dma_size); return (MSG_ERROR); } /* setup single tag buffer */ memset(msg, 0, sizeof(*msg)); msg->hdr.buf_size = sizeof(*msg); msg->hdr.code = BCM2835_MBOX_CODE_REQ; msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_TEMPERATURE; msg->tag_hdr.val_buf_size = sizeof(msg->body); msg->tag_hdr.val_len = sizeof(msg->body.req); msg->body.req.temperature_id = 0; msg->end_tag = 0; /* call mailbox property */ err = bcm2835_mbox_call_prop(sc); if (err) { device_printf(sc->dev, "can't get temperature\n"); return (MSG_ERROR); } /* result (temperature of degree C) */ value = (int)msg->body.resp.value; DPRINTF("value = %d\n", value); return (value); } static int sysctl_bcm2835_cpufreq_arm_freq(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ VC_LOCK(sc); err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM, val); VC_UNLOCK(sc); if (err == MSG_ERROR) { device_printf(sc->dev, "set clock arm_freq error\n"); return (EIO); } DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_core_freq(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ VC_LOCK(sc); err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, val); if (err == MSG_ERROR) { VC_UNLOCK(sc); device_printf(sc->dev, "set clock core_freq error\n"); return (EIO); } VC_UNLOCK(sc); DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_sdram_freq(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ VC_LOCK(sc); err = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM, val); VC_UNLOCK(sc); if (err == MSG_ERROR) { device_printf(sc->dev, "set clock sdram_freq error\n"); return (EIO); } DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_turbo(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_turbo(sc); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ if (val > 0) sc->turbo_mode = BCM2835_MBOX_TURBO_ON; else sc->turbo_mode = BCM2835_MBOX_TURBO_OFF; VC_LOCK(sc); err = bcm2835_cpufreq_set_turbo(sc, sc->turbo_mode); VC_UNLOCK(sc); if (err == MSG_ERROR) { device_printf(sc->dev, "set turbo error\n"); return (EIO); } DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_voltage_core(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) return (EINVAL); sc->voltage_core = val; VC_LOCK(sc); err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE, sc->voltage_core); VC_UNLOCK(sc); if (err == MSG_ERROR) { device_printf(sc->dev, "set voltage core error\n"); return (EIO); } DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_voltage_sdram_c(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) return (EINVAL); sc->voltage_sdram_c = val; VC_LOCK(sc); err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C, sc->voltage_sdram_c); VC_UNLOCK(sc); if (err == MSG_ERROR) { device_printf(sc->dev, "set voltage sdram_c error\n"); return (EIO); } DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_voltage_sdram_i(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) return (EINVAL); sc->voltage_sdram_i = val; VC_LOCK(sc); err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I, sc->voltage_sdram_i); VC_UNLOCK(sc); if (err == MSG_ERROR) { device_printf(sc->dev, "set voltage sdram_i error\n"); return (EIO); } DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_voltage_sdram_p(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) return (EINVAL); sc->voltage_sdram_p = val; VC_LOCK(sc); err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P, sc->voltage_sdram_p); VC_UNLOCK(sc); if (err == MSG_ERROR) { device_printf(sc->dev, "set voltage sdram_p error\n"); return (EIO); } DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_voltage_sdram(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* multiple write only */ if (!req->newptr) return (EINVAL); val = 0; err = sysctl_handle_int(oidp, &val, 0, req); if (err) return (err); /* write request */ if (val > MAX_OVER_VOLTAGE || val < MIN_OVER_VOLTAGE) return (EINVAL); sc->voltage_sdram = val; VC_LOCK(sc); err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C, val); if (err == MSG_ERROR) { VC_UNLOCK(sc); device_printf(sc->dev, "set voltage sdram_c error\n"); return (EIO); } err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I, val); if (err == MSG_ERROR) { VC_UNLOCK(sc); device_printf(sc->dev, "set voltage sdram_i error\n"); return (EIO); } err = bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P, val); if (err == MSG_ERROR) { VC_UNLOCK(sc); device_printf(sc->dev, "set voltage sdram_p error\n"); return (EIO); } VC_UNLOCK(sc); DELAY(TRANSITION_LATENCY); return (0); } static int sysctl_bcm2835_cpufreq_temperature(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_temperature(sc); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ return (EINVAL); } static int sysctl_bcm2835_devcpu_temperature(SYSCTL_HANDLER_ARGS) { struct bcm2835_cpufreq_softc *sc = arg1; int val; int err; /* get realtime value */ VC_LOCK(sc); val = bcm2835_cpufreq_get_temperature(sc); VC_UNLOCK(sc); if (val == MSG_ERROR) return (EIO); /* 1/1000 celsius (raw) to 1/10 kelvin */ - val = RAW2K(val) * 10; + val = val / 100 + TZ_ZEROC; err = sysctl_handle_int(oidp, &val, 0, req); if (err || !req->newptr) /* error || read request */ return (err); /* write request */ return (EINVAL); } static void bcm2835_cpufreq_init(void *arg) { struct bcm2835_cpufreq_softc *sc = arg; struct sysctl_ctx_list *ctx; device_t cpu; int arm_freq, core_freq, sdram_freq; int arm_max_freq, arm_min_freq, core_max_freq, core_min_freq; int sdram_max_freq, sdram_min_freq; int voltage_core, voltage_sdram_c, voltage_sdram_i, voltage_sdram_p; int max_voltage_core, min_voltage_core; int max_voltage_sdram_c, min_voltage_sdram_c; int max_voltage_sdram_i, min_voltage_sdram_i; int max_voltage_sdram_p, min_voltage_sdram_p; int turbo, temperature; VC_LOCK(sc); /* current clock */ arm_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); core_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE); sdram_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM); /* max/min clock */ arm_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); arm_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); core_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE); core_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE); sdram_max_freq = bcm2835_cpufreq_get_max_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM); sdram_min_freq = bcm2835_cpufreq_get_min_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM); /* turbo mode */ turbo = bcm2835_cpufreq_get_turbo(sc); if (turbo > 0) sc->turbo_mode = BCM2835_MBOX_TURBO_ON; else sc->turbo_mode = BCM2835_MBOX_TURBO_OFF; /* voltage */ voltage_core = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE); voltage_sdram_c = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C); voltage_sdram_i = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I); voltage_sdram_p = bcm2835_cpufreq_get_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P); /* current values (offset from 1.2V) */ sc->voltage_core = voltage_core; sc->voltage_sdram = voltage_sdram_c; sc->voltage_sdram_c = voltage_sdram_c; sc->voltage_sdram_i = voltage_sdram_i; sc->voltage_sdram_p = voltage_sdram_p; /* max/min voltage */ max_voltage_core = bcm2835_cpufreq_get_max_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE); min_voltage_core = bcm2835_cpufreq_get_min_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE); max_voltage_sdram_c = bcm2835_cpufreq_get_max_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C); max_voltage_sdram_i = bcm2835_cpufreq_get_max_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I); max_voltage_sdram_p = bcm2835_cpufreq_get_max_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P); min_voltage_sdram_c = bcm2835_cpufreq_get_min_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_C); min_voltage_sdram_i = bcm2835_cpufreq_get_min_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_I); min_voltage_sdram_p = bcm2835_cpufreq_get_min_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_SDRAM_P); /* temperature */ temperature = bcm2835_cpufreq_get_temperature(sc); /* show result */ if (cpufreq_verbose || bootverbose) { device_printf(sc->dev, "Boot settings:\n"); device_printf(sc->dev, "current ARM %dMHz, Core %dMHz, SDRAM %dMHz, Turbo %s\n", HZ2MHZ(arm_freq), HZ2MHZ(core_freq), HZ2MHZ(sdram_freq), (sc->turbo_mode == BCM2835_MBOX_TURBO_ON) ? "ON" : "OFF"); device_printf(sc->dev, "max/min ARM %d/%dMHz, Core %d/%dMHz, SDRAM %d/%dMHz\n", HZ2MHZ(arm_max_freq), HZ2MHZ(arm_min_freq), HZ2MHZ(core_max_freq), HZ2MHZ(core_min_freq), HZ2MHZ(sdram_max_freq), HZ2MHZ(sdram_min_freq)); device_printf(sc->dev, "current Core %dmV, SDRAM_C %dmV, SDRAM_I %dmV, " "SDRAM_P %dmV\n", OFFSET2MVOLT(voltage_core), OFFSET2MVOLT(voltage_sdram_c), OFFSET2MVOLT(voltage_sdram_i), OFFSET2MVOLT(voltage_sdram_p)); device_printf(sc->dev, "max/min Core %d/%dmV, SDRAM_C %d/%dmV, SDRAM_I %d/%dmV, " "SDRAM_P %d/%dmV\n", OFFSET2MVOLT(max_voltage_core), OFFSET2MVOLT(min_voltage_core), OFFSET2MVOLT(max_voltage_sdram_c), OFFSET2MVOLT(min_voltage_sdram_c), OFFSET2MVOLT(max_voltage_sdram_i), OFFSET2MVOLT(min_voltage_sdram_i), OFFSET2MVOLT(max_voltage_sdram_p), OFFSET2MVOLT(min_voltage_sdram_p)); device_printf(sc->dev, "Temperature %d.%dC\n", (temperature / 1000), (temperature % 1000) / 100); } else { /* !cpufreq_verbose && !bootverbose */ device_printf(sc->dev, "ARM %dMHz, Core %dMHz, SDRAM %dMHz, Turbo %s\n", HZ2MHZ(arm_freq), HZ2MHZ(core_freq), HZ2MHZ(sdram_freq), (sc->turbo_mode == BCM2835_MBOX_TURBO_ON) ? "ON" : "OFF"); } /* keep in softc (MHz/mV) */ sc->arm_max_freq = HZ2MHZ(arm_max_freq); sc->arm_min_freq = HZ2MHZ(arm_min_freq); sc->core_max_freq = HZ2MHZ(core_max_freq); sc->core_min_freq = HZ2MHZ(core_min_freq); sc->sdram_max_freq = HZ2MHZ(sdram_max_freq); sc->sdram_min_freq = HZ2MHZ(sdram_min_freq); sc->max_voltage_core = OFFSET2MVOLT(max_voltage_core); sc->min_voltage_core = OFFSET2MVOLT(min_voltage_core); /* if turbo is on, set to max values */ if (sc->turbo_mode == BCM2835_MBOX_TURBO_ON) { bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM, arm_max_freq); DELAY(TRANSITION_LATENCY); bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, core_max_freq); DELAY(TRANSITION_LATENCY); bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM, sdram_max_freq); DELAY(TRANSITION_LATENCY); } else { bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM, arm_min_freq); DELAY(TRANSITION_LATENCY); bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, core_min_freq); DELAY(TRANSITION_LATENCY); bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM, sdram_min_freq); DELAY(TRANSITION_LATENCY); } VC_UNLOCK(sc); /* add human readable temperature to dev.cpu node */ cpu = device_get_parent(sc->dev); if (cpu != NULL) { ctx = device_get_sysctl_ctx(cpu); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0, sysctl_bcm2835_devcpu_temperature, "IK", "Current SoC temperature"); } /* release this hook (continue boot) */ config_intrhook_disestablish(&sc->init_hook); } static void bcm2835_cpufreq_identify(driver_t *driver, device_t parent) { DPRINTF("driver=%p, parent=%p\n", driver, parent); if (device_find_child(parent, "bcm2835_cpufreq", -1) != NULL) return; if (BUS_ADD_CHILD(parent, 0, "bcm2835_cpufreq", -1) == NULL) device_printf(parent, "add child failed\n"); } static int bcm2835_cpufreq_probe(device_t dev) { device_set_desc(dev, "CPU Frequency Control"); return (0); } static void bcm2835_cpufreq_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) { bus_addr_t *addr; if (err) return; addr = (bus_addr_t *)arg; *addr = PHYS_TO_VCBUS(segs[0].ds_addr); } static int bcm2835_cpufreq_attach(device_t dev) { struct bcm2835_cpufreq_softc *sc; struct sysctl_oid *oid; int err; /* set self dev */ sc = device_get_softc(dev); sc->dev = dev; /* initial values */ sc->arm_max_freq = -1; sc->arm_min_freq = -1; sc->core_max_freq = -1; sc->core_min_freq = -1; sc->sdram_max_freq = -1; sc->sdram_min_freq = -1; sc->max_voltage_core = 0; sc->min_voltage_core = 0; /* create VC mbox buffer */ sc->dma_size = PAGE_SIZE; err = bus_dma_tag_create( bus_get_dma_tag(sc->dev), PAGE_SIZE, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ sc->dma_size, 1, /* maxsize, nsegments */ sc->dma_size, 0, /* maxsegsize, flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->dma_tag); if (err) { device_printf(dev, "can't create DMA tag\n"); return (ENXIO); } err = bus_dmamem_alloc(sc->dma_tag, (void **)&sc->dma_buf, 0, &sc->dma_map); if (err) { bus_dma_tag_destroy(sc->dma_tag); device_printf(dev, "can't allocate dmamem\n"); return (ENXIO); } err = bus_dmamap_load(sc->dma_tag, sc->dma_map, sc->dma_buf, sc->dma_size, bcm2835_cpufreq_cb, &sc->dma_phys, 0); if (err) { bus_dmamem_free(sc->dma_tag, sc->dma_buf, sc->dma_map); bus_dma_tag_destroy(sc->dma_tag); device_printf(dev, "can't load DMA map\n"); return (ENXIO); } /* OK, ready to use VC buffer */ /* setup sysctl at first device */ if (device_get_unit(dev) == 0) { sysctl_ctx_init(&bcm2835_sysctl_ctx); /* create node for hw.cpufreq */ oid = SYSCTL_ADD_NODE(&bcm2835_sysctl_ctx, SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "cpufreq", CTLFLAG_RD, NULL, ""); /* Frequency (Hz) */ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "arm_freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_bcm2835_cpufreq_arm_freq, "IU", "ARM frequency (Hz)"); SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "core_freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_bcm2835_cpufreq_core_freq, "IU", "Core frequency (Hz)"); SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "sdram_freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_bcm2835_cpufreq_sdram_freq, "IU", "SDRAM frequency (Hz)"); /* Turbo state */ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "turbo", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_bcm2835_cpufreq_turbo, "IU", "Disables dynamic clocking"); /* Voltage (offset from 1.2V in units of 0.025V) */ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "voltage_core", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_bcm2835_cpufreq_voltage_core, "I", "ARM/GPU core voltage" "(offset from 1.2V in units of 0.025V)"); SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "voltage_sdram", CTLTYPE_INT | CTLFLAG_WR, sc, 0, sysctl_bcm2835_cpufreq_voltage_sdram, "I", "SDRAM voltage (offset from 1.2V in units of 0.025V)"); /* Voltage individual SDRAM */ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "voltage_sdram_c", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_bcm2835_cpufreq_voltage_sdram_c, "I", "SDRAM controller voltage" "(offset from 1.2V in units of 0.025V)"); SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "voltage_sdram_i", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_bcm2835_cpufreq_voltage_sdram_i, "I", "SDRAM I/O voltage (offset from 1.2V in units of 0.025V)"); SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "voltage_sdram_p", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_bcm2835_cpufreq_voltage_sdram_p, "I", "SDRAM phy voltage (offset from 1.2V in units of 0.025V)"); /* Temperature */ SYSCTL_ADD_PROC(&bcm2835_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0, sysctl_bcm2835_cpufreq_temperature, "I", "SoC temperature (thousandths of a degree C)"); } /* ARM->VC lock */ sema_init(&vc_sema, 1, "vcsema"); /* register callback for using mbox when interrupts are enabled */ sc->init_hook.ich_func = bcm2835_cpufreq_init; sc->init_hook.ich_arg = sc; if (config_intrhook_establish(&sc->init_hook) != 0) { bus_dmamap_unload(sc->dma_tag, sc->dma_map); bus_dmamem_free(sc->dma_tag, sc->dma_buf, sc->dma_map); bus_dma_tag_destroy(sc->dma_tag); device_printf(dev, "config_intrhook_establish failed\n"); return (ENOMEM); } /* this device is controlled by cpufreq(4) */ cpufreq_register(dev); return (0); } static int bcm2835_cpufreq_detach(device_t dev) { struct bcm2835_cpufreq_softc *sc; sc = device_get_softc(dev); sema_destroy(&vc_sema); if (sc->dma_phys != 0) bus_dmamap_unload(sc->dma_tag, sc->dma_map); if (sc->dma_buf != NULL) bus_dmamem_free(sc->dma_tag, sc->dma_buf, sc->dma_map); if (sc->dma_tag != NULL) bus_dma_tag_destroy(sc->dma_tag); return (cpufreq_unregister(dev)); } static int bcm2835_cpufreq_set(device_t dev, const struct cf_setting *cf) { struct bcm2835_cpufreq_softc *sc; uint32_t rate_hz, rem; int cur_freq, resp_freq, arm_freq, min_freq, core_freq; if (cf == NULL || cf->freq < 0) return (EINVAL); sc = device_get_softc(dev); /* setting clock (Hz) */ rate_hz = (uint32_t)MHZ2HZ(cf->freq); rem = rate_hz % HZSTEP; rate_hz -= rem; if (rate_hz == 0) return (EINVAL); /* adjust min freq */ min_freq = sc->arm_min_freq; if (sc->turbo_mode != BCM2835_MBOX_TURBO_ON) if (min_freq > cpufreq_lowest_freq) min_freq = cpufreq_lowest_freq; if (rate_hz < MHZ2HZ(min_freq) || rate_hz > MHZ2HZ(sc->arm_max_freq)) return (EINVAL); /* set new value and verify it */ VC_LOCK(sc); cur_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); resp_freq = bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM, rate_hz); DELAY(TRANSITION_LATENCY); arm_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); /* * if non-turbo and lower than or equal min_freq, * clock down core and sdram to default first. */ if (sc->turbo_mode != BCM2835_MBOX_TURBO_ON) { core_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE); if (rate_hz > MHZ2HZ(sc->arm_min_freq)) { bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, MHZ2HZ(sc->core_max_freq)); DELAY(TRANSITION_LATENCY); bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM, MHZ2HZ(sc->sdram_max_freq)); DELAY(TRANSITION_LATENCY); } else { if (sc->core_min_freq < DEFAULT_CORE_FREQUENCY && core_freq > DEFAULT_CORE_FREQUENCY) { /* first, down to 250, then down to min */ DELAY(TRANSITION_LATENCY); bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, MHZ2HZ(DEFAULT_CORE_FREQUENCY)); DELAY(TRANSITION_LATENCY); /* reset core voltage */ bcm2835_cpufreq_set_voltage(sc, BCM2835_MBOX_VOLTAGE_ID_CORE, 0); DELAY(TRANSITION_LATENCY); } bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_CORE, MHZ2HZ(sc->core_min_freq)); DELAY(TRANSITION_LATENCY); bcm2835_cpufreq_set_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_SDRAM, MHZ2HZ(sc->sdram_min_freq)); DELAY(TRANSITION_LATENCY); } } VC_UNLOCK(sc); if (resp_freq < 0 || arm_freq < 0 || resp_freq != arm_freq) { device_printf(dev, "wrong freq\n"); return (EIO); } DPRINTF("cpufreq: %d -> %d\n", cur_freq, arm_freq); return (0); } static int bcm2835_cpufreq_get(device_t dev, struct cf_setting *cf) { struct bcm2835_cpufreq_softc *sc; int arm_freq; if (cf == NULL) return (EINVAL); sc = device_get_softc(dev); memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf)); cf->dev = NULL; /* get cuurent value */ VC_LOCK(sc); arm_freq = bcm2835_cpufreq_get_clock_rate(sc, BCM2835_MBOX_CLOCK_ID_ARM); VC_UNLOCK(sc); if (arm_freq < 0) { device_printf(dev, "can't get clock\n"); return (EINVAL); } /* CPU clock in MHz or 100ths of a percent. */ cf->freq = HZ2MHZ(arm_freq); /* Voltage in mV. */ cf->volts = CPUFREQ_VAL_UNKNOWN; /* Power consumed in mW. */ cf->power = CPUFREQ_VAL_UNKNOWN; /* Transition latency in us. */ cf->lat = TRANSITION_LATENCY; /* Driver providing this setting. */ cf->dev = dev; return (0); } static int bcm2835_cpufreq_make_freq_list(device_t dev, struct cf_setting *sets, int *count) { struct bcm2835_cpufreq_softc *sc; int freq, min_freq, volts, rem; int idx; sc = device_get_softc(dev); freq = sc->arm_max_freq; min_freq = sc->arm_min_freq; /* adjust head freq to STEP */ rem = freq % MHZSTEP; freq -= rem; if (freq < min_freq) freq = min_freq; /* if non-turbo, add extra low freq */ if (sc->turbo_mode != BCM2835_MBOX_TURBO_ON) if (min_freq > cpufreq_lowest_freq) min_freq = cpufreq_lowest_freq; /* from freq to min_freq */ for (idx = 0; idx < *count && freq >= min_freq; idx++) { if (freq > sc->arm_min_freq) volts = sc->max_voltage_core; else volts = sc->min_voltage_core; sets[idx].freq = freq; sets[idx].volts = volts; sets[idx].lat = TRANSITION_LATENCY; sets[idx].dev = dev; freq -= MHZSTEP; } *count = ++idx; return (0); } static int bcm2835_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count) { struct bcm2835_cpufreq_softc *sc; if (sets == NULL || count == NULL) return (EINVAL); sc = device_get_softc(dev); if (sc->arm_min_freq < 0 || sc->arm_max_freq < 0) { printf("device is not configured\n"); return (EINVAL); } /* fill data with unknown value */ memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count)); /* create new array up to count */ bcm2835_cpufreq_make_freq_list(dev, sets, count); return (0); } static int bcm2835_cpufreq_type(device_t dev, int *type) { if (type == NULL) return (EINVAL); *type = CPUFREQ_TYPE_ABSOLUTE; return (0); } static device_method_t bcm2835_cpufreq_methods[] = { /* Device interface */ DEVMETHOD(device_identify, bcm2835_cpufreq_identify), DEVMETHOD(device_probe, bcm2835_cpufreq_probe), DEVMETHOD(device_attach, bcm2835_cpufreq_attach), DEVMETHOD(device_detach, bcm2835_cpufreq_detach), /* cpufreq interface */ DEVMETHOD(cpufreq_drv_set, bcm2835_cpufreq_set), DEVMETHOD(cpufreq_drv_get, bcm2835_cpufreq_get), DEVMETHOD(cpufreq_drv_settings, bcm2835_cpufreq_settings), DEVMETHOD(cpufreq_drv_type, bcm2835_cpufreq_type), DEVMETHOD_END }; static devclass_t bcm2835_cpufreq_devclass; static driver_t bcm2835_cpufreq_driver = { "bcm2835_cpufreq", bcm2835_cpufreq_methods, sizeof(struct bcm2835_cpufreq_softc), }; DRIVER_MODULE(bcm2835_cpufreq, cpu, bcm2835_cpufreq_driver, bcm2835_cpufreq_devclass, 0, 0); Index: projects/ifnet/sys/arm/ti/cpsw/if_cpsw.c =================================================================== --- projects/ifnet/sys/arm/ti/cpsw/if_cpsw.c (revision 277209) +++ projects/ifnet/sys/arm/ti/cpsw/if_cpsw.c (revision 277210) @@ -1,2197 +1,2197 @@ /*- * Copyright (c) 2012 Damjan Marion * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * TI Common Platform Ethernet Switch (CPSW) Driver * Found in TI8148 "DaVinci" and AM335x "Sitara" SoCs. * * This controller is documented in the AM335x Technical Reference * Manual, in the TMS320DM814x DaVinci Digital Video Processors TRM * and in the TMS320C6452 3 Port Switch Ethernet Subsystem TRM. * * It is basically a single Ethernet port (port 0) wired internally to * a 3-port store-and-forward switch connected to two independent * "sliver" controllers (port 1 and port 2). You can operate the * controller in a variety of different ways by suitably configuring * the slivers and the Address Lookup Engine (ALE) that routes packets * between the ports. * * This code was developed and tested on a BeagleBone with * an AM335x SoC. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "if_cpswreg.h" #include "if_cpswvar.h" #include #include "miibus_if.h" /* Device probe/attach/detach. */ static int cpsw_probe(device_t); static void cpsw_init_slots(struct cpsw_softc *); static int cpsw_attach(device_t); static void cpsw_free_slot(struct cpsw_softc *, struct cpsw_slot *); static int cpsw_detach(device_t); /* Device Init/shutdown. */ static void cpsw_init(void *); static void cpsw_init_locked(void *); static int cpsw_shutdown(device_t); static void cpsw_shutdown_locked(struct cpsw_softc *); /* Device Suspend/Resume. */ static int cpsw_suspend(device_t); static int cpsw_resume(device_t); /* Ioctl. */ static int cpsw_ioctl(struct ifnet *, u_long command, caddr_t data); static int cpsw_miibus_readreg(device_t, int phy, int reg); static int cpsw_miibus_writereg(device_t, int phy, int reg, int value); /* Send/Receive packets. */ static void cpsw_intr_rx(void *arg); static struct mbuf *cpsw_rx_dequeue(struct cpsw_softc *); static void cpsw_rx_enqueue(struct cpsw_softc *); static void cpsw_start(struct ifnet *); static void cpsw_tx_enqueue(struct cpsw_softc *); static int cpsw_tx_dequeue(struct cpsw_softc *); /* Misc interrupts and watchdog. */ static void cpsw_intr_rx_thresh(void *); static void cpsw_intr_misc(void *); static void cpsw_tick(void *); static void cpsw_ifmedia_sts(struct ifnet *, struct ifmediareq *); static int cpsw_ifmedia_upd(struct ifnet *); static void cpsw_tx_watchdog(struct cpsw_softc *); /* ALE support */ static void cpsw_ale_read_entry(struct cpsw_softc *, uint16_t idx, uint32_t *ale_entry); static void cpsw_ale_write_entry(struct cpsw_softc *, uint16_t idx, uint32_t *ale_entry); static int cpsw_ale_mc_entry_set(struct cpsw_softc *, uint8_t portmap, uint8_t *mac); static int cpsw_ale_update_addresses(struct cpsw_softc *, int purge); static void cpsw_ale_dump_table(struct cpsw_softc *); /* Statistics and sysctls. */ static void cpsw_add_sysctls(struct cpsw_softc *); static void cpsw_stats_collect(struct cpsw_softc *); static int cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS); /* * Arbitrary limit on number of segments in an mbuf to be transmitted. * Packets with more segments than this will be defragmented before * they are queued. */ #define CPSW_TXFRAGS 8 /* * TODO: The CPSW subsystem (CPSW_SS) can drive two independent PHYs * as separate Ethernet ports. To properly support this, we should * break this into two separate devices: a CPSW_SS device that owns * the interrupts and actually talks to the CPSW hardware, and a * separate CPSW Ethernet child device for each Ethernet port. The RX * interrupt, for example, would be part of CPSW_SS; it would receive * a packet, note the input port, and then dispatch it to the child * device's interface queue. Similarly for transmit. * * It's not clear to me whether the device tree should be restructured * with a cpsw_ss node and two child nodes. That would allow specifying * MAC addresses for each port, for example, but might be overkill. * * Unfortunately, I don't have hardware right now that supports two * Ethernet ports via CPSW. */ static device_method_t cpsw_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cpsw_probe), DEVMETHOD(device_attach, cpsw_attach), DEVMETHOD(device_detach, cpsw_detach), DEVMETHOD(device_shutdown, cpsw_shutdown), DEVMETHOD(device_suspend, cpsw_suspend), DEVMETHOD(device_resume, cpsw_resume), /* MII interface */ DEVMETHOD(miibus_readreg, cpsw_miibus_readreg), DEVMETHOD(miibus_writereg, cpsw_miibus_writereg), { 0, 0 } }; static driver_t cpsw_driver = { "cpsw", cpsw_methods, sizeof(struct cpsw_softc), }; static devclass_t cpsw_devclass; DRIVER_MODULE(cpsw, simplebus, cpsw_driver, cpsw_devclass, 0, 0); DRIVER_MODULE(miibus, cpsw, miibus_driver, miibus_devclass, 0, 0); MODULE_DEPEND(cpsw, ether, 1, 1, 1); MODULE_DEPEND(cpsw, miibus, 1, 1, 1); static struct resource_spec res_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { SYS_RES_IRQ, 1, RF_ACTIVE | RF_SHAREABLE }, { SYS_RES_IRQ, 2, RF_ACTIVE | RF_SHAREABLE }, { SYS_RES_IRQ, 3, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; /* Number of entries here must match size of stats * array in struct cpsw_softc. */ static struct cpsw_stat { int reg; char *oid; } cpsw_stat_sysctls[CPSW_SYSCTL_COUNT] = { {0x00, "GoodRxFrames"}, {0x04, "BroadcastRxFrames"}, {0x08, "MulticastRxFrames"}, {0x0C, "PauseRxFrames"}, {0x10, "RxCrcErrors"}, {0x14, "RxAlignErrors"}, {0x18, "OversizeRxFrames"}, {0x1c, "RxJabbers"}, {0x20, "ShortRxFrames"}, {0x24, "RxFragments"}, {0x30, "RxOctets"}, {0x34, "GoodTxFrames"}, {0x38, "BroadcastTxFrames"}, {0x3c, "MulticastTxFrames"}, {0x40, "PauseTxFrames"}, {0x44, "DeferredTxFrames"}, {0x48, "CollisionsTxFrames"}, {0x4c, "SingleCollisionTxFrames"}, {0x50, "MultipleCollisionTxFrames"}, {0x54, "ExcessiveCollisions"}, {0x58, "LateCollisions"}, {0x5c, "TxUnderrun"}, {0x60, "CarrierSenseErrors"}, {0x64, "TxOctets"}, {0x68, "RxTx64OctetFrames"}, {0x6c, "RxTx65to127OctetFrames"}, {0x70, "RxTx128to255OctetFrames"}, {0x74, "RxTx256to511OctetFrames"}, {0x78, "RxTx512to1024OctetFrames"}, {0x7c, "RxTx1024upOctetFrames"}, {0x80, "NetOctets"}, {0x84, "RxStartOfFrameOverruns"}, {0x88, "RxMiddleOfFrameOverruns"}, {0x8c, "RxDmaOverruns"} }; /* * Basic debug support. */ #define IF_DEBUG(sc) if (sc->cpsw_if_flags & IFF_DEBUG) static void cpsw_debugf_head(const char *funcname) { int t = (int)(time_second % (24 * 60 * 60)); printf("%02d:%02d:%02d %s ", t / (60 * 60), (t / 60) % 60, t % 60, funcname); } #include static void cpsw_debugf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf("\n"); } #define CPSW_DEBUGF(a) do { \ IF_DEBUG(sc) { \ cpsw_debugf_head(__func__); \ cpsw_debugf a; \ } \ } while (0) /* * Locking macros */ #define CPSW_TX_LOCK(sc) do { \ mtx_assert(&(sc)->rx.lock, MA_NOTOWNED); \ mtx_lock(&(sc)->tx.lock); \ } while (0) #define CPSW_TX_UNLOCK(sc) mtx_unlock(&(sc)->tx.lock) #define CPSW_TX_LOCK_ASSERT(sc) mtx_assert(&(sc)->tx.lock, MA_OWNED) #define CPSW_RX_LOCK(sc) do { \ mtx_assert(&(sc)->tx.lock, MA_NOTOWNED); \ mtx_lock(&(sc)->rx.lock); \ } while (0) #define CPSW_RX_UNLOCK(sc) mtx_unlock(&(sc)->rx.lock) #define CPSW_RX_LOCK_ASSERT(sc) mtx_assert(&(sc)->rx.lock, MA_OWNED) #define CPSW_GLOBAL_LOCK(sc) do { \ if ((mtx_owned(&(sc)->tx.lock) ? 1 : 0) != \ (mtx_owned(&(sc)->rx.lock) ? 1 : 0)) { \ panic("cpsw deadlock possibility detection!"); \ } \ mtx_lock(&(sc)->tx.lock); \ mtx_lock(&(sc)->rx.lock); \ } while (0) #define CPSW_GLOBAL_UNLOCK(sc) do { \ CPSW_RX_UNLOCK(sc); \ CPSW_TX_UNLOCK(sc); \ } while (0) #define CPSW_GLOBAL_LOCK_ASSERT(sc) do { \ CPSW_TX_LOCK_ASSERT(sc); \ CPSW_RX_LOCK_ASSERT(sc); \ } while (0) /* * Read/Write macros */ #define cpsw_read_4(sc, reg) bus_read_4(sc->res[0], reg) #define cpsw_write_4(sc, reg, val) bus_write_4(sc->res[0], reg, val) #define cpsw_cpdma_bd_offset(i) (CPSW_CPPI_RAM_OFFSET + ((i)*16)) #define cpsw_cpdma_bd_paddr(sc, slot) \ BUS_SPACE_PHYSADDR(sc->res[0], slot->bd_offset) #define cpsw_cpdma_read_bd(sc, slot, val) \ bus_read_region_4(sc->res[0], slot->bd_offset, (uint32_t *) val, 4) #define cpsw_cpdma_write_bd(sc, slot, val) \ bus_write_region_4(sc->res[0], slot->bd_offset, (uint32_t *) val, 4) #define cpsw_cpdma_write_bd_next(sc, slot, next_slot) \ cpsw_write_4(sc, slot->bd_offset, cpsw_cpdma_bd_paddr(sc, next_slot)) #define cpsw_cpdma_read_bd_flags(sc, slot) \ bus_read_2(sc->res[0], slot->bd_offset + 14) #define cpsw_write_hdp_slot(sc, queue, slot) \ cpsw_write_4(sc, (queue)->hdp_offset, cpsw_cpdma_bd_paddr(sc, slot)) #define CP_OFFSET (CPSW_CPDMA_TX_CP(0) - CPSW_CPDMA_TX_HDP(0)) #define cpsw_read_cp(sc, queue) \ cpsw_read_4(sc, (queue)->hdp_offset + CP_OFFSET) #define cpsw_write_cp(sc, queue, val) \ cpsw_write_4(sc, (queue)->hdp_offset + CP_OFFSET, (val)) #define cpsw_write_cp_slot(sc, queue, slot) \ cpsw_write_cp(sc, queue, cpsw_cpdma_bd_paddr(sc, slot)) #if 0 /* XXX temporary function versions for debugging. */ static void cpsw_write_hdp_slotX(struct cpsw_softc *sc, struct cpsw_queue *queue, struct cpsw_slot *slot) { uint32_t reg = queue->hdp_offset; uint32_t v = cpsw_cpdma_bd_paddr(sc, slot); CPSW_DEBUGF(("HDP <=== 0x%08x (was 0x%08x)", v, cpsw_read_4(sc, reg))); cpsw_write_4(sc, reg, v); } static void cpsw_write_cp_slotX(struct cpsw_softc *sc, struct cpsw_queue *queue, struct cpsw_slot *slot) { uint32_t v = cpsw_cpdma_bd_paddr(sc, slot); CPSW_DEBUGF(("CP <=== 0x%08x (expecting 0x%08x)", v, cpsw_read_cp(sc, queue))); cpsw_write_cp(sc, queue, v); } #endif /* * Expanded dump routines for verbose debugging. */ static void cpsw_dump_slot(struct cpsw_softc *sc, struct cpsw_slot *slot) { static const char *flags[] = {"SOP", "EOP", "Owner", "EOQ", "TDownCmplt", "PassCRC", "Long", "Short", "MacCtl", "Overrun", "PktErr1", "PortEn/PktErr0", "RxVlanEncap", "Port2", "Port1", "Port0"}; struct cpsw_cpdma_bd bd; const char *sep; int i; cpsw_cpdma_read_bd(sc, slot, &bd); printf("BD Addr: 0x%08x Next: 0x%08x\n", cpsw_cpdma_bd_paddr(sc, slot), bd.next); printf(" BufPtr: 0x%08x BufLen: 0x%08x\n", bd.bufptr, bd.buflen); printf(" BufOff: 0x%08x PktLen: 0x%08x\n", bd.bufoff, bd.pktlen); printf(" Flags: "); sep = ""; for (i = 0; i < 16; ++i) { if (bd.flags & (1 << (15 - i))) { printf("%s%s", sep, flags[i]); sep = ","; } } printf("\n"); if (slot->mbuf) { printf(" Ether: %14D\n", - (char *)(slot->mbuf->m_hdr.mh_data), " "); + (char *)(slot->mbuf->m_data), " "); printf(" Packet: %16D\n", - (char *)(slot->mbuf->m_hdr.mh_data) + 14, " "); + (char *)(slot->mbuf->m_data) + 14, " "); } } #define CPSW_DUMP_SLOT(cs, slot) do { \ IF_DEBUG(sc) { \ cpsw_dump_slot(sc, slot); \ } \ } while (0) static void cpsw_dump_queue(struct cpsw_softc *sc, struct cpsw_slots *q) { struct cpsw_slot *slot; int i = 0; int others = 0; STAILQ_FOREACH(slot, q, next) { if (i > 4) ++others; else cpsw_dump_slot(sc, slot); ++i; } if (others) printf(" ... and %d more.\n", others); printf("\n"); } #define CPSW_DUMP_QUEUE(sc, q) do { \ IF_DEBUG(sc) { \ cpsw_dump_queue(sc, q); \ } \ } while (0) /* * * Device Probe, Attach, Detach. * */ static int cpsw_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,cpsw")) return (ENXIO); device_set_desc(dev, "3-port Switch Ethernet Subsystem"); return (BUS_PROBE_DEFAULT); } static void cpsw_init_slots(struct cpsw_softc *sc) { struct cpsw_slot *slot; int i; STAILQ_INIT(&sc->avail); /* Put the slot descriptors onto the global avail list. */ for (i = 0; i < sizeof(sc->_slots) / sizeof(sc->_slots[0]); i++) { slot = &sc->_slots[i]; slot->bd_offset = cpsw_cpdma_bd_offset(i); STAILQ_INSERT_TAIL(&sc->avail, slot, next); } } /* * bind an interrupt, add the relevant info to sc->interrupts */ static int cpsw_attach_interrupt(struct cpsw_softc *sc, struct resource *res, driver_intr_t *handler, const char *description) { void **pcookie; int error; sc->interrupts[sc->interrupt_count].res = res; sc->interrupts[sc->interrupt_count].description = description; pcookie = &sc->interrupts[sc->interrupt_count].ih_cookie; error = bus_setup_intr(sc->dev, res, INTR_TYPE_NET | INTR_MPSAFE, NULL, *handler, sc, pcookie); if (error) device_printf(sc->dev, "could not setup %s\n", description); else ++sc->interrupt_count; return (error); } /* * teardown everything in sc->interrupts. */ static void cpsw_detach_interrupts(struct cpsw_softc *sc) { int error; int i; for (i = 0; i < sizeof(sc->interrupts) / sizeof(sc->interrupts[0]); ++i) { if (!sc->interrupts[i].ih_cookie) continue; error = bus_teardown_intr(sc->dev, sc->interrupts[i].res, sc->interrupts[i].ih_cookie); if (error) device_printf(sc->dev, "could not release %s\n", sc->interrupts[i].description); sc->interrupts[i].ih_cookie = NULL; } } static int cpsw_add_slots(struct cpsw_softc *sc, struct cpsw_queue *queue, int requested) { const int max_slots = sizeof(sc->_slots) / sizeof(sc->_slots[0]); struct cpsw_slot *slot; int i; if (requested < 0) requested = max_slots; for (i = 0; i < requested; ++i) { slot = STAILQ_FIRST(&sc->avail); if (slot == NULL) return (0); if (bus_dmamap_create(sc->mbuf_dtag, 0, &slot->dmamap)) { if_printf(sc->ifp, "failed to create dmamap\n"); return (ENOMEM); } STAILQ_REMOVE_HEAD(&sc->avail, next); STAILQ_INSERT_TAIL(&queue->avail, slot, next); ++queue->avail_queue_len; ++queue->queue_slots; } return (0); } static int cpsw_attach(device_t dev) { bus_dma_segment_t segs[1]; struct cpsw_softc *sc = device_get_softc(dev); struct mii_softc *miisc; struct ifnet *ifp; void *phy_sc; int error, phy, nsegs; uint32_t reg; CPSW_DEBUGF(("")); getbinuptime(&sc->attach_uptime); sc->dev = dev; sc->node = ofw_bus_get_node(dev); /* Get phy address from fdt */ if (fdt_get_phyaddr(sc->node, sc->dev, &phy, &phy_sc) != 0) { device_printf(dev, "failed to get PHY address from FDT\n"); return (ENXIO); } /* Initialize mutexes */ mtx_init(&sc->tx.lock, device_get_nameunit(dev), "cpsw TX lock", MTX_DEF); mtx_init(&sc->rx.lock, device_get_nameunit(dev), "cpsw RX lock", MTX_DEF); /* Allocate IO and IRQ resources */ error = bus_alloc_resources(dev, res_spec, sc->res); if (error) { device_printf(dev, "could not allocate resources\n"); cpsw_detach(dev); return (ENXIO); } reg = cpsw_read_4(sc, CPSW_SS_IDVER); device_printf(dev, "CPSW SS Version %d.%d (%d)\n", (reg >> 8 & 0x7), reg & 0xFF, (reg >> 11) & 0x1F); cpsw_add_sysctls(sc); /* Allocate a busdma tag and DMA safe memory for mbufs. */ error = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filtfunc, filtfuncarg */ MCLBYTES, CPSW_TXFRAGS, /* maxsize, nsegments */ MCLBYTES, 0, /* maxsegsz, flags */ NULL, NULL, /* lockfunc, lockfuncarg */ &sc->mbuf_dtag); /* dmatag */ if (error) { device_printf(dev, "bus_dma_tag_create failed\n"); cpsw_detach(dev); return (error); } /* Allocate network interface */ ifp = sc->ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(dev, "if_alloc() failed\n"); cpsw_detach(dev); return (ENOMEM); } /* Allocate the null mbuf and pre-sync it. */ sc->null_mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); - memset(sc->null_mbuf->m_hdr.mh_data, 0, sc->null_mbuf->m_ext.ext_size); + memset(sc->null_mbuf->m_data, 0, sc->null_mbuf->m_ext.ext_size); bus_dmamap_create(sc->mbuf_dtag, 0, &sc->null_mbuf_dmamap); bus_dmamap_load_mbuf_sg(sc->mbuf_dtag, sc->null_mbuf_dmamap, sc->null_mbuf, segs, &nsegs, BUS_DMA_NOWAIT); bus_dmamap_sync(sc->mbuf_dtag, sc->null_mbuf_dmamap, BUS_DMASYNC_PREWRITE); sc->null_mbuf_paddr = segs[0].ds_addr; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_softc = sc; ifp->if_flags = IFF_SIMPLEX | IFF_MULTICAST | IFF_BROADCAST; ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_HWCSUM; //FIXME VLAN? ifp->if_capenable = ifp->if_capabilities; ifp->if_init = cpsw_init; ifp->if_start = cpsw_start; ifp->if_ioctl = cpsw_ioctl; cpsw_init_slots(sc); /* Allocate slots to TX and RX queues. */ STAILQ_INIT(&sc->rx.avail); STAILQ_INIT(&sc->rx.active); STAILQ_INIT(&sc->tx.avail); STAILQ_INIT(&sc->tx.active); // For now: 128 slots to TX, rest to RX. // XXX TODO: start with 32/64 and grow dynamically based on demand. if (cpsw_add_slots(sc, &sc->tx, 128) || cpsw_add_slots(sc, &sc->rx, -1)) { device_printf(dev, "failed to allocate dmamaps\n"); cpsw_detach(dev); return (ENOMEM); } device_printf(dev, "Initial queue size TX=%d RX=%d\n", sc->tx.queue_slots, sc->rx.queue_slots); ifp->if_snd.ifq_drv_maxlen = sc->tx.queue_slots; IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); IFQ_SET_READY(&ifp->if_snd); sc->tx.hdp_offset = CPSW_CPDMA_TX_HDP(0); sc->rx.hdp_offset = CPSW_CPDMA_RX_HDP(0); /* Get high part of MAC address from control module (mac_id0_hi) */ /* TODO: Get MAC ID1 as well as MAC ID0. */ ti_scm_reg_read_4(0x634, ®); sc->mac_addr[0] = reg & 0xFF; sc->mac_addr[1] = (reg >> 8) & 0xFF; sc->mac_addr[2] = (reg >> 16) & 0xFF; sc->mac_addr[3] = (reg >> 24) & 0xFF; /* Get low part of MAC address from control module (mac_id0_lo) */ ti_scm_reg_read_4(0x630, ®); sc->mac_addr[4] = reg & 0xFF; sc->mac_addr[5] = (reg >> 8) & 0xFF; /* Initialze MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */ /* TODO Calculate MDCLK=CLK/(CLKDIV+1) */ cpsw_write_4(sc, MDIOCONTROL, 1 << 30 | 1 << 18 | 0xFF); /* Clear ALE */ cpsw_write_4(sc, CPSW_ALE_CONTROL, 1 << 30); /* Attach PHY(s) */ error = mii_attach(dev, &sc->miibus, ifp, cpsw_ifmedia_upd, cpsw_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); if (error) { device_printf(dev, "attaching PHYs failed\n"); cpsw_detach(dev); return (error); } sc->mii = device_get_softc(sc->miibus); /* Tell the MAC where to find the PHY so autoneg works */ miisc = LIST_FIRST(&sc->mii->mii_phys); /* Select PHY and enable interrupts */ cpsw_write_4(sc, MDIOUSERPHYSEL0, 1 << 6 | (miisc->mii_phy & 0x1F)); /* Note: We don't use sc->res[3] (TX interrupt) */ if (cpsw_attach_interrupt(sc, sc->res[1], cpsw_intr_rx_thresh, "CPSW RX threshold interrupt") || cpsw_attach_interrupt(sc, sc->res[2], cpsw_intr_rx, "CPSW RX interrupt") || cpsw_attach_interrupt(sc, sc->res[4], cpsw_intr_misc, "CPSW misc interrupt")) { cpsw_detach(dev); return (ENXIO); } ether_ifattach(ifp, sc->mac_addr); callout_init(&sc->watchdog.callout, 0); return (0); } static void cpsw_free_slot(struct cpsw_softc *sc, struct cpsw_slot *slot) { int error; if (slot->dmamap) { error = bus_dmamap_destroy(sc->mbuf_dtag, slot->dmamap); KASSERT(error == 0, ("Mapping still active")); slot->dmamap = NULL; } if (slot->mbuf) { m_freem(slot->mbuf); slot->mbuf = NULL; } } static int cpsw_detach(device_t dev) { struct cpsw_softc *sc = device_get_softc(dev); int error, i; CPSW_DEBUGF(("")); /* Stop controller and free TX queue */ if (device_is_attached(dev)) { ether_ifdetach(sc->ifp); CPSW_GLOBAL_LOCK(sc); cpsw_shutdown_locked(sc); CPSW_GLOBAL_UNLOCK(sc); callout_drain(&sc->watchdog.callout); } bus_generic_detach(dev); if (sc->miibus) device_delete_child(dev, sc->miibus); /* Stop and release all interrupts */ cpsw_detach_interrupts(sc); /* Free dmamaps and mbufs */ for (i = 0; i < sizeof(sc->_slots) / sizeof(sc->_slots[0]); ++i) cpsw_free_slot(sc, &sc->_slots[i]); if (sc->null_mbuf_dmamap) { error = bus_dmamap_destroy(sc->mbuf_dtag, sc->null_mbuf_dmamap); KASSERT(error == 0, ("Mapping still active")); } if (sc->null_mbuf) m_freem(sc->null_mbuf); /* Free DMA tag */ error = bus_dma_tag_destroy(sc->mbuf_dtag); KASSERT(error == 0, ("Unable to destroy DMA tag")); /* Free IO memory handler */ bus_release_resources(dev, res_spec, sc->res); if (sc->ifp != NULL) if_free(sc->ifp); /* Destroy mutexes */ mtx_destroy(&sc->rx.lock); mtx_destroy(&sc->tx.lock); return (0); } /* * * Init/Shutdown. * */ static void cpsw_reset(struct cpsw_softc *sc) { int i; /* Reset RMII/RGMII wrapper. */ cpsw_write_4(sc, CPSW_WR_SOFT_RESET, 1); while (cpsw_read_4(sc, CPSW_WR_SOFT_RESET) & 1) ; /* Disable TX and RX interrupts for all cores. */ for (i = 0; i < 3; ++i) { cpsw_write_4(sc, CPSW_WR_C_RX_THRESH_EN(i), 0x00); cpsw_write_4(sc, CPSW_WR_C_TX_EN(i), 0x00); cpsw_write_4(sc, CPSW_WR_C_RX_EN(i), 0x00); cpsw_write_4(sc, CPSW_WR_C_MISC_EN(i), 0x00); } /* Reset CPSW subsystem. */ cpsw_write_4(sc, CPSW_SS_SOFT_RESET, 1); while (cpsw_read_4(sc, CPSW_SS_SOFT_RESET) & 1) ; /* Reset Sliver port 1 and 2 */ for (i = 0; i < 2; i++) { /* Reset */ cpsw_write_4(sc, CPSW_SL_SOFT_RESET(i), 1); while (cpsw_read_4(sc, CPSW_SL_SOFT_RESET(i)) & 1) ; } /* Reset DMA controller. */ cpsw_write_4(sc, CPSW_CPDMA_SOFT_RESET, 1); while (cpsw_read_4(sc, CPSW_CPDMA_SOFT_RESET) & 1) ; /* Disable TX & RX DMA */ cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 0); cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 0); /* Clear all queues. */ for (i = 0; i < 8; i++) { cpsw_write_4(sc, CPSW_CPDMA_TX_HDP(i), 0); cpsw_write_4(sc, CPSW_CPDMA_RX_HDP(i), 0); cpsw_write_4(sc, CPSW_CPDMA_TX_CP(i), 0); cpsw_write_4(sc, CPSW_CPDMA_RX_CP(i), 0); } /* Clear all interrupt Masks */ cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_CLEAR, 0xFFFFFFFF); cpsw_write_4(sc, CPSW_CPDMA_TX_INTMASK_CLEAR, 0xFFFFFFFF); } static void cpsw_init(void *arg) { struct cpsw_softc *sc = arg; CPSW_DEBUGF(("")); CPSW_GLOBAL_LOCK(sc); cpsw_init_locked(arg); CPSW_GLOBAL_UNLOCK(sc); } static void cpsw_init_locked(void *arg) { struct ifnet *ifp; struct cpsw_softc *sc = arg; struct cpsw_slot *slot; uint32_t i; CPSW_DEBUGF(("")); ifp = sc->ifp; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) return; getbinuptime(&sc->init_uptime); /* Reset the controller. */ cpsw_reset(sc); /* Enable ALE */ cpsw_write_4(sc, CPSW_ALE_CONTROL, 1 << 31 | 1 << 4); /* Init Sliver port 1 and 2 */ for (i = 0; i < 2; i++) { /* Set Slave Mapping */ cpsw_write_4(sc, CPSW_SL_RX_PRI_MAP(i), 0x76543210); cpsw_write_4(sc, CPSW_PORT_P_TX_PRI_MAP(i + 1), 0x33221100); cpsw_write_4(sc, CPSW_SL_RX_MAXLEN(i), 0x5f2); /* Set MACCONTROL for ports 0,1: IFCTL_B(16), IFCTL_A(15), GMII_EN(5), FULLDUPLEX(1) */ /* TODO: Docs claim that IFCTL_B and IFCTL_A do the same thing? */ /* Huh? Docs call bit 0 "Loopback" some places, "FullDuplex" others. */ cpsw_write_4(sc, CPSW_SL_MACCONTROL(i), 1 << 15 | 1 << 5 | 1); } /* Set Host Port Mapping */ cpsw_write_4(sc, CPSW_PORT_P0_CPDMA_TX_PRI_MAP, 0x76543210); cpsw_write_4(sc, CPSW_PORT_P0_CPDMA_RX_CH_MAP, 0); /* Initialize ALE: all ports set to forwarding(3), initialize addrs */ for (i = 0; i < 3; i++) cpsw_write_4(sc, CPSW_ALE_PORTCTL(i), 3); cpsw_ale_update_addresses(sc, 1); cpsw_write_4(sc, CPSW_SS_PTYPE, 0); /* Enable statistics for ports 0, 1 and 2 */ cpsw_write_4(sc, CPSW_SS_STAT_PORT_EN, 7); /* Experiment: Turn off flow control */ /* This seems to fix the watchdog resets that have plagued earlier versions of this driver; I'm not yet sure if there are negative effects yet. */ cpsw_write_4(sc, CPSW_SS_FLOW_CONTROL, 0); /* Make IP hdr aligned with 4 */ cpsw_write_4(sc, CPSW_CPDMA_RX_BUFFER_OFFSET, 2); /* Initialize RX Buffer Descriptors */ cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), 0); /* Enable TX & RX DMA */ cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 1); cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 1); /* Enable Interrupts for core 0 */ cpsw_write_4(sc, CPSW_WR_C_RX_THRESH_EN(0), 0xFF); cpsw_write_4(sc, CPSW_WR_C_RX_EN(0), 0xFF); cpsw_write_4(sc, CPSW_WR_C_MISC_EN(0), 0x3F); /* Enable host Error Interrupt */ cpsw_write_4(sc, CPSW_CPDMA_DMA_INTMASK_SET, 3); /* Enable interrupts for RX Channel 0 */ cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_SET, 1); /* Initialze MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */ /* TODO Calculate MDCLK=CLK/(CLKDIV+1) */ cpsw_write_4(sc, MDIOCONTROL, 1 << 30 | 1 << 18 | 0xFF); /* Select MII in GMII_SEL, Internal Delay mode */ //ti_scm_reg_write_4(0x650, 0); /* Initialize active queues. */ slot = STAILQ_FIRST(&sc->tx.active); if (slot != NULL) cpsw_write_hdp_slot(sc, &sc->tx, slot); slot = STAILQ_FIRST(&sc->rx.active); if (slot != NULL) cpsw_write_hdp_slot(sc, &sc->rx, slot); cpsw_rx_enqueue(sc); /* Activate network interface */ sc->rx.running = 1; sc->tx.running = 1; sc->watchdog.timer = 0; callout_reset(&sc->watchdog.callout, hz, cpsw_tick, sc); sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } static int cpsw_shutdown(device_t dev) { struct cpsw_softc *sc = device_get_softc(dev); CPSW_DEBUGF(("")); CPSW_GLOBAL_LOCK(sc); cpsw_shutdown_locked(sc); CPSW_GLOBAL_UNLOCK(sc); return (0); } static void cpsw_rx_teardown_locked(struct cpsw_softc *sc) { struct mbuf *received, *next; int i = 0; CPSW_DEBUGF(("starting RX teardown")); cpsw_write_4(sc, CPSW_CPDMA_RX_TEARDOWN, 0); for (;;) { received = cpsw_rx_dequeue(sc); CPSW_GLOBAL_UNLOCK(sc); while (received != NULL) { next = received->m_nextpkt; received->m_nextpkt = NULL; (*sc->ifp->if_input)(sc->ifp, received); received = next; } CPSW_GLOBAL_LOCK(sc); if (!sc->rx.running) { CPSW_DEBUGF(("finished RX teardown (%d retries)", i)); return; } if (++i > 10) { if_printf(sc->ifp, "Unable to cleanly shutdown receiver\n"); return; } DELAY(10); } } static void cpsw_tx_teardown_locked(struct cpsw_softc *sc) { int i = 0; CPSW_DEBUGF(("starting TX teardown")); cpsw_write_4(sc, CPSW_CPDMA_TX_TEARDOWN, 0); cpsw_tx_dequeue(sc); while (sc->tx.running && ++i < 10) { DELAY(10); cpsw_tx_dequeue(sc); } if (sc->tx.running) if_printf(sc->ifp, "Unable to cleanly shutdown transmitter\n"); CPSW_DEBUGF(("finished TX teardown (%d retries, %d idle buffers)", i, sc->tx.active_queue_len)); } static void cpsw_shutdown_locked(struct cpsw_softc *sc) { struct ifnet *ifp; CPSW_DEBUGF(("")); CPSW_GLOBAL_LOCK_ASSERT(sc); ifp = sc->ifp; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; /* Disable interface */ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; ifp->if_drv_flags |= IFF_DRV_OACTIVE; /* Stop ticker */ callout_stop(&sc->watchdog.callout); /* Tear down the RX/TX queues. */ cpsw_rx_teardown_locked(sc); cpsw_tx_teardown_locked(sc); /* Capture stats before we reset controller. */ cpsw_stats_collect(sc); cpsw_reset(sc); } /* * Suspend/Resume. */ static int cpsw_suspend(device_t dev) { struct cpsw_softc *sc = device_get_softc(dev); CPSW_DEBUGF(("")); CPSW_GLOBAL_LOCK(sc); cpsw_shutdown_locked(sc); CPSW_GLOBAL_UNLOCK(sc); return (0); } static int cpsw_resume(device_t dev) { struct cpsw_softc *sc = device_get_softc(dev); CPSW_DEBUGF(("UNIMPLEMENTED")); return (0); } /* * * IOCTL * */ static void cpsw_set_promisc(struct cpsw_softc *sc, int set) { /* * Enabling promiscuous mode requires two bits of work: First, * ALE_BYPASS needs to be enabled. That disables the ALE * forwarding logic and causes every packet to be sent to the * host port. That makes us promiscuous wrt received packets. * * With ALE forwarding disabled, the transmitter needs to set * an explicit output port on every packet to route it to the * correct egress. This should be doable for systems such as * BeagleBone where only one egress port is actually wired to * a PHY. If you have both egress ports wired up, life gets a * lot more interesting. * * Hmmm.... NetBSD driver uses ALE_BYPASS always and doesn't * seem to set explicit egress ports. Does that mean they * are always promiscuous? */ if (set) { printf("Promiscuous mode unimplemented\n"); } } static void cpsw_set_allmulti(struct cpsw_softc *sc, int set) { if (set) { printf("All-multicast mode unimplemented\n"); } } static int cpsw_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct cpsw_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; int error; uint32_t changed; error = 0; switch (command) { case SIOCSIFFLAGS: CPSW_GLOBAL_LOCK(sc); if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { changed = ifp->if_flags ^ sc->cpsw_if_flags; CPSW_DEBUGF(("SIOCSIFFLAGS: UP & RUNNING (changed=0x%x)", changed)); if (changed & IFF_PROMISC) cpsw_set_promisc(sc, ifp->if_flags & IFF_PROMISC); if (changed & IFF_ALLMULTI) cpsw_set_allmulti(sc, ifp->if_flags & IFF_ALLMULTI); } else { CPSW_DEBUGF(("SIOCSIFFLAGS: UP but not RUNNING; starting up")); cpsw_init_locked(sc); } } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) { CPSW_DEBUGF(("SIOCSIFFLAGS: not UP but RUNNING; shutting down")); cpsw_shutdown_locked(sc); } sc->cpsw_if_flags = ifp->if_flags; CPSW_GLOBAL_UNLOCK(sc); break; case SIOCADDMULTI: cpsw_ale_update_addresses(sc, 0); break; case SIOCDELMULTI: /* Ugh. DELMULTI doesn't provide the specific address being removed, so the best we can do is remove everything and rebuild it all. */ cpsw_ale_update_addresses(sc, 1); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->mii->mii_media, command); break; default: error = ether_ioctl(ifp, command, data); } return (error); } /* * * MIIBUS * */ static int cpsw_miibus_ready(struct cpsw_softc *sc) { uint32_t r, retries = CPSW_MIIBUS_RETRIES; while (--retries) { r = cpsw_read_4(sc, MDIOUSERACCESS0); if ((r & 1 << 31) == 0) return 1; DELAY(CPSW_MIIBUS_DELAY); } return 0; } static int cpsw_miibus_readreg(device_t dev, int phy, int reg) { struct cpsw_softc *sc = device_get_softc(dev); uint32_t cmd, r; if (!cpsw_miibus_ready(sc)) { device_printf(dev, "MDIO not ready to read\n"); return 0; } /* Set GO, reg, phy */ cmd = 1 << 31 | (reg & 0x1F) << 21 | (phy & 0x1F) << 16; cpsw_write_4(sc, MDIOUSERACCESS0, cmd); if (!cpsw_miibus_ready(sc)) { device_printf(dev, "MDIO timed out during read\n"); return 0; } r = cpsw_read_4(sc, MDIOUSERACCESS0); if((r & 1 << 29) == 0) { device_printf(dev, "Failed to read from PHY.\n"); r = 0; } return (r & 0xFFFF); } static int cpsw_miibus_writereg(device_t dev, int phy, int reg, int value) { struct cpsw_softc *sc = device_get_softc(dev); uint32_t cmd; if (!cpsw_miibus_ready(sc)) { device_printf(dev, "MDIO not ready to write\n"); return 0; } /* Set GO, WRITE, reg, phy, and value */ cmd = 3 << 30 | (reg & 0x1F) << 21 | (phy & 0x1F) << 16 | (value & 0xFFFF); cpsw_write_4(sc, MDIOUSERACCESS0, cmd); if (!cpsw_miibus_ready(sc)) { device_printf(dev, "MDIO timed out during write\n"); return 0; } if((cpsw_read_4(sc, MDIOUSERACCESS0) & (1 << 29)) == 0) device_printf(dev, "Failed to write to PHY.\n"); return 0; } /* * * Transmit/Receive Packets. * */ static void cpsw_intr_rx(void *arg) { struct cpsw_softc *sc = arg; struct mbuf *received, *next; CPSW_RX_LOCK(sc); received = cpsw_rx_dequeue(sc); cpsw_rx_enqueue(sc); cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 1); CPSW_RX_UNLOCK(sc); while (received != NULL) { next = received->m_nextpkt; received->m_nextpkt = NULL; (*sc->ifp->if_input)(sc->ifp, received); received = next; } } static struct mbuf * cpsw_rx_dequeue(struct cpsw_softc *sc) { struct cpsw_cpdma_bd bd; struct cpsw_slot *slot; struct ifnet *ifp; struct mbuf *mb_head, *mb_tail; int removed = 0; ifp = sc->ifp; mb_head = mb_tail = NULL; /* Pull completed packets off hardware RX queue. */ while ((slot = STAILQ_FIRST(&sc->rx.active)) != NULL) { cpsw_cpdma_read_bd(sc, slot, &bd); if (bd.flags & CPDMA_BD_OWNER) break; /* Still in use by hardware */ CPSW_DEBUGF(("Removing received packet from RX queue")); ++removed; STAILQ_REMOVE_HEAD(&sc->rx.active, next); STAILQ_INSERT_TAIL(&sc->rx.avail, slot, next); bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); if (bd.flags & CPDMA_BD_TDOWNCMPLT) { CPSW_DEBUGF(("RX teardown in progress")); m_freem(slot->mbuf); slot->mbuf = NULL; cpsw_write_cp(sc, &sc->rx, 0xfffffffc); sc->rx.running = 0; break; } cpsw_write_cp_slot(sc, &sc->rx, slot); /* Set up mbuf */ /* TODO: track SOP/EOP bits to assemble a full mbuf out of received fragments. */ - slot->mbuf->m_hdr.mh_data += bd.bufoff; - slot->mbuf->m_hdr.mh_len = bd.pktlen - 4; + slot->mbuf->m_data += bd.bufoff; + slot->mbuf->m_len = bd.pktlen - 4; slot->mbuf->m_pkthdr.len = bd.pktlen - 4; slot->mbuf->m_flags |= M_PKTHDR; slot->mbuf->m_pkthdr.rcvif = ifp; slot->mbuf->m_nextpkt = NULL; if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) { /* check for valid CRC by looking into pkt_err[5:4] */ if ((bd.flags & CPDMA_BD_PKT_ERR_MASK) == 0) { slot->mbuf->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; slot->mbuf->m_pkthdr.csum_flags |= CSUM_IP_VALID; slot->mbuf->m_pkthdr.csum_data = 0xffff; } } /* Add mbuf to packet list to be returned. */ if (mb_tail) { mb_tail->m_nextpkt = slot->mbuf; } else { mb_head = slot->mbuf; } mb_tail = slot->mbuf; slot->mbuf = NULL; } if (removed != 0) { sc->rx.queue_removes += removed; sc->rx.active_queue_len -= removed; sc->rx.avail_queue_len += removed; if (sc->rx.avail_queue_len > sc->rx.max_avail_queue_len) sc->rx.max_avail_queue_len = sc->rx.avail_queue_len; } return (mb_head); } static void cpsw_rx_enqueue(struct cpsw_softc *sc) { bus_dma_segment_t seg[1]; struct cpsw_cpdma_bd bd; struct ifnet *ifp = sc->ifp; struct cpsw_slots tmpqueue = STAILQ_HEAD_INITIALIZER(tmpqueue); struct cpsw_slot *slot, *prev_slot = NULL; struct cpsw_slot *last_old_slot, *first_new_slot; int error, nsegs, added = 0; /* Register new mbufs with hardware. */ while ((slot = STAILQ_FIRST(&sc->rx.avail)) != NULL) { if (slot->mbuf == NULL) { slot->mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (slot->mbuf == NULL) { if_printf(sc->ifp, "Unable to fill RX queue\n"); break; } slot->mbuf->m_len = slot->mbuf->m_pkthdr.len = slot->mbuf->m_ext.ext_size; } error = bus_dmamap_load_mbuf_sg(sc->mbuf_dtag, slot->dmamap, slot->mbuf, seg, &nsegs, BUS_DMA_NOWAIT); KASSERT(nsegs == 1, ("More than one segment (nsegs=%d)", nsegs)); KASSERT(error == 0, ("DMA error (error=%d)", error)); if (error != 0 || nsegs != 1) { if_printf(ifp, "%s: Can't prep RX buf for DMA (nsegs=%d, error=%d)\n", __func__, nsegs, error); bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); m_freem(slot->mbuf); slot->mbuf = NULL; break; } bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_PREREAD); /* Create and submit new rx descriptor*/ bd.next = 0; bd.bufptr = seg->ds_addr; bd.bufoff = 0; bd.buflen = MCLBYTES - 1; bd.pktlen = bd.buflen; bd.flags = CPDMA_BD_OWNER; cpsw_cpdma_write_bd(sc, slot, &bd); ++added; if (prev_slot != NULL) cpsw_cpdma_write_bd_next(sc, prev_slot, slot); prev_slot = slot; STAILQ_REMOVE_HEAD(&sc->rx.avail, next); sc->rx.avail_queue_len--; STAILQ_INSERT_TAIL(&tmpqueue, slot, next); } if (added == 0) return; CPSW_DEBUGF(("Adding %d buffers to RX queue", added)); /* Link new entries to hardware RX queue. */ last_old_slot = STAILQ_LAST(&sc->rx.active, cpsw_slot, next); first_new_slot = STAILQ_FIRST(&tmpqueue); STAILQ_CONCAT(&sc->rx.active, &tmpqueue); if (first_new_slot == NULL) { return; } else if (last_old_slot == NULL) { /* Start a fresh queue. */ cpsw_write_hdp_slot(sc, &sc->rx, first_new_slot); } else { /* Add buffers to end of current queue. */ cpsw_cpdma_write_bd_next(sc, last_old_slot, first_new_slot); /* If underrun, restart queue. */ if (cpsw_cpdma_read_bd_flags(sc, last_old_slot) & CPDMA_BD_EOQ) { cpsw_write_hdp_slot(sc, &sc->rx, first_new_slot); } } sc->rx.queue_adds += added; sc->rx.active_queue_len += added; if (sc->rx.active_queue_len > sc->rx.max_active_queue_len) { sc->rx.max_active_queue_len = sc->rx.active_queue_len; } } static void cpsw_start(struct ifnet *ifp) { struct cpsw_softc *sc = ifp->if_softc; CPSW_TX_LOCK(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && sc->tx.running) { cpsw_tx_enqueue(sc); cpsw_tx_dequeue(sc); } CPSW_TX_UNLOCK(sc); } static void cpsw_tx_enqueue(struct cpsw_softc *sc) { bus_dma_segment_t segs[CPSW_TXFRAGS]; struct cpsw_cpdma_bd bd; struct cpsw_slots tmpqueue = STAILQ_HEAD_INITIALIZER(tmpqueue); struct cpsw_slot *slot, *prev_slot = NULL; struct cpsw_slot *last_old_slot, *first_new_slot; struct mbuf *m0; int error, nsegs, seg, added = 0, padlen; /* Pull pending packets from IF queue and prep them for DMA. */ while ((slot = STAILQ_FIRST(&sc->tx.avail)) != NULL) { IF_DEQUEUE(&sc->ifp->if_snd, m0); if (m0 == NULL) break; slot->mbuf = m0; padlen = ETHER_MIN_LEN - slot->mbuf->m_pkthdr.len; if (padlen < 0) padlen = 0; /* Create mapping in DMA memory */ error = bus_dmamap_load_mbuf_sg(sc->mbuf_dtag, slot->dmamap, slot->mbuf, segs, &nsegs, BUS_DMA_NOWAIT); /* If the packet is too fragmented, try to simplify. */ if (error == EFBIG || (error == 0 && nsegs + (padlen > 0 ? 1 : 0) > sc->tx.avail_queue_len)) { bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); if (padlen > 0) /* May as well add padding. */ m_append(slot->mbuf, padlen, - sc->null_mbuf->m_hdr.mh_data); + sc->null_mbuf->m_data); m0 = m_defrag(slot->mbuf, M_NOWAIT); if (m0 == NULL) { if_printf(sc->ifp, "Can't defragment packet; dropping\n"); m_freem(slot->mbuf); } else { CPSW_DEBUGF(("Requeueing defragmented packet")); IF_PREPEND(&sc->ifp->if_snd, m0); } slot->mbuf = NULL; continue; } if (error != 0) { if_printf(sc->ifp, "%s: Can't setup DMA (error=%d), dropping packet\n", __func__, error); bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); m_freem(slot->mbuf); slot->mbuf = NULL; break; } bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_PREWRITE); CPSW_DEBUGF(("Queueing TX packet: %d segments + %d pad bytes", nsegs, padlen)); /* If there is only one segment, the for() loop * gets skipped and the single buffer gets set up * as both SOP and EOP. */ /* Start by setting up the first buffer */ bd.next = 0; bd.bufptr = segs[0].ds_addr; bd.bufoff = 0; bd.buflen = segs[0].ds_len; bd.pktlen = m_length(slot->mbuf, NULL) + padlen; bd.flags = CPDMA_BD_SOP | CPDMA_BD_OWNER; for (seg = 1; seg < nsegs; ++seg) { /* Save the previous buffer (which isn't EOP) */ cpsw_cpdma_write_bd(sc, slot, &bd); if (prev_slot != NULL) cpsw_cpdma_write_bd_next(sc, prev_slot, slot); prev_slot = slot; STAILQ_REMOVE_HEAD(&sc->tx.avail, next); sc->tx.avail_queue_len--; STAILQ_INSERT_TAIL(&tmpqueue, slot, next); ++added; slot = STAILQ_FIRST(&sc->tx.avail); /* Setup next buffer (which isn't SOP) */ bd.next = 0; bd.bufptr = segs[seg].ds_addr; bd.bufoff = 0; bd.buflen = segs[seg].ds_len; bd.pktlen = 0; bd.flags = CPDMA_BD_OWNER; } /* Save the final buffer. */ if (padlen <= 0) bd.flags |= CPDMA_BD_EOP; cpsw_cpdma_write_bd(sc, slot, &bd); if (prev_slot != NULL) cpsw_cpdma_write_bd_next(sc, prev_slot, slot); prev_slot = slot; STAILQ_REMOVE_HEAD(&sc->tx.avail, next); sc->tx.avail_queue_len--; STAILQ_INSERT_TAIL(&tmpqueue, slot, next); ++added; if (padlen > 0) { slot = STAILQ_FIRST(&sc->tx.avail); STAILQ_REMOVE_HEAD(&sc->tx.avail, next); sc->tx.avail_queue_len--; STAILQ_INSERT_TAIL(&tmpqueue, slot, next); ++added; /* Setup buffer of null pad bytes (definitely EOP) */ cpsw_cpdma_write_bd_next(sc, prev_slot, slot); prev_slot = slot; bd.next = 0; bd.bufptr = sc->null_mbuf_paddr; bd.bufoff = 0; bd.buflen = padlen; bd.pktlen = 0; bd.flags = CPDMA_BD_EOP | CPDMA_BD_OWNER; cpsw_cpdma_write_bd(sc, slot, &bd); ++nsegs; } if (nsegs > sc->tx.longest_chain) sc->tx.longest_chain = nsegs; // TODO: Should we defer the BPF tap until // after all packets are queued? BPF_MTAP(sc->ifp, m0); } /* Attach the list of new buffers to the hardware TX queue. */ last_old_slot = STAILQ_LAST(&sc->tx.active, cpsw_slot, next); first_new_slot = STAILQ_FIRST(&tmpqueue); STAILQ_CONCAT(&sc->tx.active, &tmpqueue); if (first_new_slot == NULL) { return; } else if (last_old_slot == NULL) { /* Start a fresh queue. */ cpsw_write_hdp_slot(sc, &sc->tx, first_new_slot); } else { /* Add buffers to end of current queue. */ cpsw_cpdma_write_bd_next(sc, last_old_slot, first_new_slot); /* If underrun, restart queue. */ if (cpsw_cpdma_read_bd_flags(sc, last_old_slot) & CPDMA_BD_EOQ) { cpsw_write_hdp_slot(sc, &sc->tx, first_new_slot); } } sc->tx.queue_adds += added; sc->tx.active_queue_len += added; if (sc->tx.active_queue_len > sc->tx.max_active_queue_len) { sc->tx.max_active_queue_len = sc->tx.active_queue_len; } } static int cpsw_tx_dequeue(struct cpsw_softc *sc) { struct cpsw_slot *slot, *last_removed_slot = NULL; uint32_t flags, removed = 0; slot = STAILQ_FIRST(&sc->tx.active); if (slot == NULL && cpsw_read_cp(sc, &sc->tx) == 0xfffffffc) { CPSW_DEBUGF(("TX teardown of an empty queue")); cpsw_write_cp(sc, &sc->tx, 0xfffffffc); sc->tx.running = 0; return (0); } /* Pull completed buffers off the hardware TX queue. */ while (slot != NULL) { flags = cpsw_cpdma_read_bd_flags(sc, slot); if (flags & CPDMA_BD_OWNER) break; /* Hardware is still using this packet. */ CPSW_DEBUGF(("TX removing completed packet")); bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); m_freem(slot->mbuf); slot->mbuf = NULL; /* Dequeue any additional buffers used by this packet. */ while (slot != NULL && slot->mbuf == NULL) { STAILQ_REMOVE_HEAD(&sc->tx.active, next); STAILQ_INSERT_TAIL(&sc->tx.avail, slot, next); ++removed; last_removed_slot = slot; slot = STAILQ_FIRST(&sc->tx.active); } /* TearDown complete is only marked on the SOP for the packet. */ if (flags & CPDMA_BD_TDOWNCMPLT) { CPSW_DEBUGF(("TX teardown in progress")); cpsw_write_cp(sc, &sc->tx, 0xfffffffc); // TODO: Increment a count of dropped TX packets sc->tx.running = 0; break; } } if (removed != 0) { cpsw_write_cp_slot(sc, &sc->tx, last_removed_slot); sc->tx.queue_removes += removed; sc->tx.active_queue_len -= removed; sc->tx.avail_queue_len += removed; if (sc->tx.avail_queue_len > sc->tx.max_avail_queue_len) sc->tx.max_avail_queue_len = sc->tx.avail_queue_len; } return (removed); } /* * * Miscellaneous interrupts. * */ static void cpsw_intr_rx_thresh(void *arg) { struct cpsw_softc *sc = arg; uint32_t stat = cpsw_read_4(sc, CPSW_WR_C_RX_THRESH_STAT(0)); CPSW_DEBUGF(("stat=%x", stat)); cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 0); } static void cpsw_intr_misc_host_error(struct cpsw_softc *sc) { uint32_t intstat; uint32_t dmastat; int txerr, rxerr, txchan, rxchan; printf("\n\n"); device_printf(sc->dev, "HOST ERROR: PROGRAMMING ERROR DETECTED BY HARDWARE\n"); printf("\n\n"); intstat = cpsw_read_4(sc, CPSW_CPDMA_DMA_INTSTAT_MASKED); device_printf(sc->dev, "CPSW_CPDMA_DMA_INTSTAT_MASKED=0x%x\n", intstat); dmastat = cpsw_read_4(sc, CPSW_CPDMA_DMASTATUS); device_printf(sc->dev, "CPSW_CPDMA_DMASTATUS=0x%x\n", dmastat); txerr = (dmastat >> 20) & 15; txchan = (dmastat >> 16) & 7; rxerr = (dmastat >> 12) & 15; rxchan = (dmastat >> 8) & 7; switch (txerr) { case 0: break; case 1: printf("SOP error on TX channel %d\n", txchan); break; case 2: printf("Ownership bit not set on SOP buffer on TX channel %d\n", txchan); break; case 3: printf("Zero Next Buffer but not EOP on TX channel %d\n", txchan); break; case 4: printf("Zero Buffer Pointer on TX channel %d\n", txchan); break; case 5: printf("Zero Buffer Length on TX channel %d\n", txchan); break; case 6: printf("Packet length error on TX channel %d\n", txchan); break; default: printf("Unknown error on TX channel %d\n", txchan); break; } if (txerr != 0) { printf("CPSW_CPDMA_TX%d_HDP=0x%x\n", txchan, cpsw_read_4(sc, CPSW_CPDMA_TX_HDP(txchan))); printf("CPSW_CPDMA_TX%d_CP=0x%x\n", txchan, cpsw_read_4(sc, CPSW_CPDMA_TX_CP(txchan))); cpsw_dump_queue(sc, &sc->tx.active); } switch (rxerr) { case 0: break; case 2: printf("Ownership bit not set on RX channel %d\n", rxchan); break; case 4: printf("Zero Buffer Pointer on RX channel %d\n", rxchan); break; case 5: printf("Zero Buffer Length on RX channel %d\n", rxchan); break; case 6: printf("Buffer offset too big on RX channel %d\n", rxchan); break; default: printf("Unknown RX error on RX channel %d\n", rxchan); break; } if (rxerr != 0) { printf("CPSW_CPDMA_RX%d_HDP=0x%x\n", rxchan, cpsw_read_4(sc,CPSW_CPDMA_RX_HDP(rxchan))); printf("CPSW_CPDMA_RX%d_CP=0x%x\n", rxchan, cpsw_read_4(sc, CPSW_CPDMA_RX_CP(rxchan))); cpsw_dump_queue(sc, &sc->rx.active); } printf("\nALE Table\n"); cpsw_ale_dump_table(sc); // XXX do something useful here?? panic("CPSW HOST ERROR INTERRUPT"); // Suppress this interrupt in the future. cpsw_write_4(sc, CPSW_CPDMA_DMA_INTMASK_CLEAR, intstat); printf("XXX HOST ERROR INTERRUPT SUPPRESSED\n"); // The watchdog will probably reset the controller // in a little while. It will probably fail again. } static void cpsw_intr_misc(void *arg) { struct cpsw_softc *sc = arg; uint32_t stat = cpsw_read_4(sc, CPSW_WR_C_MISC_STAT(0)); if (stat & 16) CPSW_DEBUGF(("Time sync event interrupt unimplemented")); if (stat & 8) cpsw_stats_collect(sc); if (stat & 4) cpsw_intr_misc_host_error(sc); if (stat & 2) CPSW_DEBUGF(("MDIO link change interrupt unimplemented")); if (stat & 1) CPSW_DEBUGF(("MDIO operation completed interrupt unimplemented")); cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 3); } /* * * Periodic Checks and Watchdog. * */ static void cpsw_tick(void *msc) { struct cpsw_softc *sc = msc; /* Check for TX timeout */ cpsw_tx_watchdog(sc); /* Check for media type change */ mii_tick(sc->mii); if(sc->cpsw_media_status != sc->mii->mii_media.ifm_media) { printf("%s: media type changed (ifm_media=%x)\n", __func__, sc->mii->mii_media.ifm_media); cpsw_ifmedia_upd(sc->ifp); } /* Schedule another timeout one second from now */ callout_reset(&sc->watchdog.callout, hz, cpsw_tick, sc); } static void cpsw_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct cpsw_softc *sc = ifp->if_softc; struct mii_data *mii; CPSW_DEBUGF(("")); CPSW_TX_LOCK(sc); mii = sc->mii; mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; CPSW_TX_UNLOCK(sc); } static int cpsw_ifmedia_upd(struct ifnet *ifp) { struct cpsw_softc *sc = ifp->if_softc; CPSW_DEBUGF(("")); if (ifp->if_flags & IFF_UP) { CPSW_GLOBAL_LOCK(sc); sc->cpsw_media_status = sc->mii->mii_media.ifm_media; mii_mediachg(sc->mii); cpsw_init_locked(sc); CPSW_GLOBAL_UNLOCK(sc); } return (0); } static void cpsw_tx_watchdog_full_reset(struct cpsw_softc *sc) { cpsw_debugf_head("CPSW watchdog"); if_printf(sc->ifp, "watchdog timeout\n"); cpsw_shutdown_locked(sc); cpsw_init_locked(sc); } static void cpsw_tx_watchdog(struct cpsw_softc *sc) { struct ifnet *ifp = sc->ifp; CPSW_GLOBAL_LOCK(sc); if (sc->tx.active_queue_len == 0 || (ifp->if_flags & IFF_UP) == 0 || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || !sc->tx.running) { sc->watchdog.timer = 0; /* Nothing to do. */ } else if (sc->tx.queue_removes > sc->tx.queue_removes_at_last_tick) { sc->watchdog.timer = 0; /* Stuff done while we weren't looking. */ } else if (cpsw_tx_dequeue(sc) > 0) { sc->watchdog.timer = 0; /* We just did something. */ } else { /* There was something to do but it didn't get done. */ ++sc->watchdog.timer; if (sc->watchdog.timer > 2) { sc->watchdog.timer = 0; if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ++sc->watchdog.resets; cpsw_tx_watchdog_full_reset(sc); } } sc->tx.queue_removes_at_last_tick = sc->tx.queue_removes; CPSW_GLOBAL_UNLOCK(sc); } /* * * ALE support routines. * */ static void cpsw_ale_read_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry) { cpsw_write_4(sc, CPSW_ALE_TBLCTL, idx & 1023); ale_entry[0] = cpsw_read_4(sc, CPSW_ALE_TBLW0); ale_entry[1] = cpsw_read_4(sc, CPSW_ALE_TBLW1); ale_entry[2] = cpsw_read_4(sc, CPSW_ALE_TBLW2); } static void cpsw_ale_write_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry) { cpsw_write_4(sc, CPSW_ALE_TBLW0, ale_entry[0]); cpsw_write_4(sc, CPSW_ALE_TBLW1, ale_entry[1]); cpsw_write_4(sc, CPSW_ALE_TBLW2, ale_entry[2]); cpsw_write_4(sc, CPSW_ALE_TBLCTL, 1 << 31 | (idx & 1023)); } static int cpsw_ale_remove_all_mc_entries(struct cpsw_softc *sc) { int i; uint32_t ale_entry[3]; /* First two entries are link address and broadcast. */ for (i = 2; i < CPSW_MAX_ALE_ENTRIES; i++) { cpsw_ale_read_entry(sc, i, ale_entry); if (((ale_entry[1] >> 28) & 3) == 1 && /* Address entry */ ((ale_entry[1] >> 8) & 1) == 1) { /* MCast link addr */ ale_entry[0] = ale_entry[1] = ale_entry[2] = 0; cpsw_ale_write_entry(sc, i, ale_entry); } } return CPSW_MAX_ALE_ENTRIES; } static int cpsw_ale_mc_entry_set(struct cpsw_softc *sc, uint8_t portmap, uint8_t *mac) { int free_index = -1, matching_index = -1, i; uint32_t ale_entry[3]; /* Find a matching entry or a free entry. */ for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) { cpsw_ale_read_entry(sc, i, ale_entry); /* Entry Type[61:60] is 0 for free entry */ if (free_index < 0 && ((ale_entry[1] >> 28) & 3) == 0) { free_index = i; } if ((((ale_entry[1] >> 8) & 0xFF) == mac[0]) && (((ale_entry[1] >> 0) & 0xFF) == mac[1]) && (((ale_entry[0] >>24) & 0xFF) == mac[2]) && (((ale_entry[0] >>16) & 0xFF) == mac[3]) && (((ale_entry[0] >> 8) & 0xFF) == mac[4]) && (((ale_entry[0] >> 0) & 0xFF) == mac[5])) { matching_index = i; break; } } if (matching_index < 0) { if (free_index < 0) return (ENOMEM); i = free_index; } /* Set MAC address */ ale_entry[0] = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; ale_entry[1] = mac[0] << 8 | mac[1]; /* Entry type[61:60] is addr entry(1), Mcast fwd state[63:62] is fw(3)*/ ale_entry[1] |= 0xd0 << 24; /* Set portmask [68:66] */ ale_entry[2] = (portmap & 7) << 2; cpsw_ale_write_entry(sc, i, ale_entry); return 0; } static void cpsw_ale_dump_table(struct cpsw_softc *sc) { int i; uint32_t ale_entry[3]; for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) { cpsw_ale_read_entry(sc, i, ale_entry); if (ale_entry[0] || ale_entry[1] || ale_entry[2]) { printf("ALE[%4u] %08x %08x %08x ", i, ale_entry[0], ale_entry[1], ale_entry[2]); printf("mac: %02x:%02x:%02x:%02x:%02x:%02x ", (ale_entry[1] >> 8) & 0xFF, (ale_entry[1] >> 0) & 0xFF, (ale_entry[0] >>24) & 0xFF, (ale_entry[0] >>16) & 0xFF, (ale_entry[0] >> 8) & 0xFF, (ale_entry[0] >> 0) & 0xFF); printf(((ale_entry[1] >> 8) & 1) ? "mcast " : "ucast "); printf("type: %u ", (ale_entry[1] >> 28) & 3); printf("port: %u ", (ale_entry[2] >> 2) & 7); printf("\n"); } } printf("\n"); } static int cpsw_ale_update_addresses(struct cpsw_softc *sc, int purge) { uint8_t *mac; uint32_t ale_entry[3]; struct ifnet *ifp = sc->ifp; struct ifmultiaddr *ifma; int i; /* Route incoming packets for our MAC address to Port 0 (host). */ /* For simplicity, keep this entry at table index 0 in the ALE. */ if_addr_rlock(ifp); mac = LLADDR((struct sockaddr_dl *)ifp->if_addr->ifa_addr); ale_entry[0] = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; ale_entry[1] = 0x10 << 24 | mac[0] << 8 | mac[1]; /* addr entry + mac */ ale_entry[2] = 0; /* port = 0 */ cpsw_ale_write_entry(sc, 0, ale_entry); /* Set outgoing MAC Address for Ports 1 and 2. */ for (i = 1; i < 3; ++i) { cpsw_write_4(sc, CPSW_PORT_P_SA_HI(i), mac[3] << 24 | mac[2] << 16 | mac[1] << 8 | mac[0]); cpsw_write_4(sc, CPSW_PORT_P_SA_LO(i), mac[5] << 8 | mac[4]); } if_addr_runlock(ifp); /* Keep the broadcast address at table entry 1. */ ale_entry[0] = 0xffffffff; /* Lower 32 bits of MAC */ ale_entry[1] = 0xd000ffff; /* FW (3 << 30), Addr entry (1 << 24), upper 16 bits of Mac */ ale_entry[2] = 0x0000001c; /* Forward to all ports */ cpsw_ale_write_entry(sc, 1, ale_entry); /* SIOCDELMULTI doesn't specify the particular address being removed, so we have to remove all and rebuild. */ if (purge) cpsw_ale_remove_all_mc_entries(sc); /* Set other multicast addrs desired. */ if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; cpsw_ale_mc_entry_set(sc, 7, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); } if_maddr_runlock(ifp); return (0); } /* * * Statistics and Sysctls. * */ #if 0 static void cpsw_stats_dump(struct cpsw_softc *sc) { int i; uint32_t r; for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) { r = cpsw_read_4(sc, CPSW_STATS_OFFSET + cpsw_stat_sysctls[i].reg); CPSW_DEBUGF(("%s: %ju + %u = %ju", cpsw_stat_sysctls[i].oid, (intmax_t)sc->shadow_stats[i], r, (intmax_t)sc->shadow_stats[i] + r)); } } #endif static void cpsw_stats_collect(struct cpsw_softc *sc) { int i; uint32_t r; CPSW_DEBUGF(("Controller shadow statistics updated.")); for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) { r = cpsw_read_4(sc, CPSW_STATS_OFFSET + cpsw_stat_sysctls[i].reg); sc->shadow_stats[i] += r; cpsw_write_4(sc, CPSW_STATS_OFFSET + cpsw_stat_sysctls[i].reg, r); } } static int cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS) { struct cpsw_softc *sc; struct cpsw_stat *stat; uint64_t result; sc = (struct cpsw_softc *)arg1; stat = &cpsw_stat_sysctls[oidp->oid_number]; result = sc->shadow_stats[oidp->oid_number]; result += cpsw_read_4(sc, CPSW_STATS_OFFSET + stat->reg); return (sysctl_handle_64(oidp, &result, 0, req)); } static int cpsw_stat_attached(SYSCTL_HANDLER_ARGS) { struct cpsw_softc *sc; struct bintime t; unsigned result; sc = (struct cpsw_softc *)arg1; getbinuptime(&t); bintime_sub(&t, &sc->attach_uptime); result = t.sec; return (sysctl_handle_int(oidp, &result, 0, req)); } static int cpsw_stat_uptime(SYSCTL_HANDLER_ARGS) { struct cpsw_softc *sc; struct bintime t; unsigned result; sc = (struct cpsw_softc *)arg1; if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) { getbinuptime(&t); bintime_sub(&t, &sc->init_uptime); result = t.sec; } else result = 0; return (sysctl_handle_int(oidp, &result, 0, req)); } static void cpsw_add_queue_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node, struct cpsw_queue *queue) { struct sysctl_oid_list *parent; parent = SYSCTL_CHILDREN(node); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "totalBuffers", CTLFLAG_RD, &queue->queue_slots, 0, "Total buffers currently assigned to this queue"); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "activeBuffers", CTLFLAG_RD, &queue->active_queue_len, 0, "Buffers currently registered with hardware controller"); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "maxActiveBuffers", CTLFLAG_RD, &queue->max_active_queue_len, 0, "Max value of activeBuffers since last driver reset"); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "availBuffers", CTLFLAG_RD, &queue->avail_queue_len, 0, "Buffers allocated to this queue but not currently " "registered with hardware controller"); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "maxAvailBuffers", CTLFLAG_RD, &queue->max_avail_queue_len, 0, "Max value of availBuffers since last driver reset"); SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "totalEnqueued", CTLFLAG_RD, &queue->queue_adds, 0, "Total buffers added to queue"); SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "totalDequeued", CTLFLAG_RD, &queue->queue_removes, 0, "Total buffers removed from queue"); SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "longestChain", CTLFLAG_RD, &queue->longest_chain, 0, "Max buffers used for a single packet"); } static void cpsw_add_watchdog_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node, struct cpsw_softc *sc) { struct sysctl_oid_list *parent; parent = SYSCTL_CHILDREN(node); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "resets", CTLFLAG_RD, &sc->watchdog.resets, 0, "Total number of watchdog resets"); } static void cpsw_add_sysctls(struct cpsw_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid *stats_node, *queue_node, *node; struct sysctl_oid_list *parent, *stats_parent, *queue_parent; int i; ctx = device_get_sysctl_ctx(sc->dev); parent = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "attachedSecs", CTLTYPE_UINT | CTLFLAG_RD, sc, 0, cpsw_stat_attached, "IU", "Time since driver attach"); SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "uptime", CTLTYPE_UINT | CTLFLAG_RD, sc, 0, cpsw_stat_uptime, "IU", "Seconds since driver init"); stats_node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "stats", CTLFLAG_RD, NULL, "CPSW Statistics"); stats_parent = SYSCTL_CHILDREN(stats_node); for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) { SYSCTL_ADD_PROC(ctx, stats_parent, i, cpsw_stat_sysctls[i].oid, CTLTYPE_U64 | CTLFLAG_RD, sc, 0, cpsw_stats_sysctl, "IU", cpsw_stat_sysctls[i].oid); } queue_node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "queue", CTLFLAG_RD, NULL, "CPSW Queue Statistics"); queue_parent = SYSCTL_CHILDREN(queue_node); node = SYSCTL_ADD_NODE(ctx, queue_parent, OID_AUTO, "tx", CTLFLAG_RD, NULL, "TX Queue Statistics"); cpsw_add_queue_sysctls(ctx, node, &sc->tx); node = SYSCTL_ADD_NODE(ctx, queue_parent, OID_AUTO, "rx", CTLFLAG_RD, NULL, "RX Queue Statistics"); cpsw_add_queue_sysctls(ctx, node, &sc->rx); node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "watchdog", CTLFLAG_RD, NULL, "Watchdog Statistics"); cpsw_add_watchdog_sysctls(ctx, node, sc); } Index: projects/ifnet/sys/boot/common/load_elf_obj.c =================================================================== --- projects/ifnet/sys/boot/common/load_elf_obj.c (revision 277209) +++ projects/ifnet/sys/boot/common/load_elf_obj.c (revision 277210) @@ -1,542 +1,543 @@ /*- * Copyright (c) 2004 Ian Dowse * Copyright (c) 1998 Michael Smith * Copyright (c) 1998 Peter Wemm * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #define FREEBSD_ELF #include #include "bootstrap.h" #define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l) #if defined(__i386__) && __ELF_WORD_SIZE == 64 #undef ELF_TARG_CLASS #undef ELF_TARG_MACH #define ELF_TARG_CLASS ELFCLASS64 #define ELF_TARG_MACH EM_X86_64 #endif typedef struct elf_file { Elf_Ehdr hdr; Elf_Shdr *e_shdr; int symtabindex; /* Index of symbol table */ int shstrindex; /* Index of section name string table */ int fd; vm_offset_t off; } *elf_file_t; static int __elfN(obj_loadimage)(struct preloaded_file *mp, elf_file_t ef, u_int64_t loadaddr); static int __elfN(obj_lookup_set)(struct preloaded_file *mp, elf_file_t ef, const char *name, Elf_Addr *startp, Elf_Addr *stopp, int *countp); static int __elfN(obj_reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len); static int __elfN(obj_parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef); static Elf_Addr __elfN(obj_symaddr)(struct elf_file *ef, Elf_Size symidx); const char *__elfN(obj_kerneltype) = "elf kernel"; const char *__elfN(obj_moduletype) = "elf obj module"; /* * Attempt to load the file (file) as an ELF module. It will be stored at * (dest), and a pointer to a module structure describing the loaded object * will be saved in (result). */ int __elfN(obj_loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result) { struct preloaded_file *fp, *kfp; struct elf_file ef; Elf_Ehdr *hdr; int err; ssize_t bytes_read; fp = NULL; bzero(&ef, sizeof(struct elf_file)); /* * Open the image, read and validate the ELF header */ if (filename == NULL) /* can't handle nameless */ return(EFTYPE); if ((ef.fd = open(filename, O_RDONLY)) == -1) return(errno); hdr = &ef.hdr; bytes_read = read(ef.fd, hdr, sizeof(*hdr)); if (bytes_read != sizeof(*hdr)) { err = EFTYPE; /* could be EIO, but may be small file */ goto oerr; } /* Is it ELF? */ if (!IS_ELF(*hdr)) { err = EFTYPE; goto oerr; } if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ hdr->e_ident[EI_DATA] != ELF_TARG_DATA || hdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ hdr->e_version != EV_CURRENT || hdr->e_machine != ELF_TARG_MACH || /* Machine ? */ hdr->e_type != ET_REL) { err = EFTYPE; goto oerr; } if (hdr->e_shnum * hdr->e_shentsize == 0 || hdr->e_shoff == 0 || hdr->e_shentsize != sizeof(Elf_Shdr)) { err = EFTYPE; goto oerr; } kfp = file_findfile(NULL, NULL); if (kfp == NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadfile: can't load module before kernel\n"); err = EPERM; goto oerr; } if (strcmp(__elfN(obj_kerneltype), kfp->f_type)) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadfile: can't load module with kernel type '%s'\n", kfp->f_type); err = EPERM; goto oerr; } if (archsw.arch_loadaddr != NULL) dest = archsw.arch_loadaddr(LOAD_ELF, hdr, dest); else dest = roundup(dest, PAGE_SIZE); /* * Ok, we think we should handle this. */ fp = file_alloc(); if (fp == NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadfile: cannot allocate module info\n"); err = EPERM; goto out; } fp->f_name = strdup(filename); fp->f_type = strdup(__elfN(obj_moduletype)); printf("%s ", filename); fp->f_size = __elfN(obj_loadimage)(fp, &ef, dest); if (fp->f_size == 0 || fp->f_addr == 0) goto ioerr; /* save exec header as metadata */ file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof(*hdr), hdr); /* Load OK, return module pointer */ *result = (struct preloaded_file *)fp; err = 0; goto out; ioerr: err = EIO; oerr: file_discard(fp); out: close(ef.fd); if (ef.e_shdr != NULL) free(ef.e_shdr); return(err); } /* * With the file (fd) open on the image, and (ehdr) containing * the Elf header, load the image at (off) */ static int __elfN(obj_loadimage)(struct preloaded_file *fp, elf_file_t ef, u_int64_t off) { Elf_Ehdr *hdr; Elf_Shdr *shdr, *cshdr, *lshdr; vm_offset_t firstaddr, lastaddr; int i, nsym, res, ret, shdrbytes, symstrindex; ret = 0; firstaddr = lastaddr = (vm_offset_t)off; hdr = &ef->hdr; ef->off = (vm_offset_t)off; /* Read in the section headers. */ shdrbytes = hdr->e_shnum * hdr->e_shentsize; shdr = alloc_pread(ef->fd, (off_t)hdr->e_shoff, shdrbytes); if (shdr == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: read section headers failed\n"); goto out; } ef->e_shdr = shdr; /* * Decide where to load everything, but don't read it yet. * We store the load address as a non-zero sh_addr value. * Start with the code/data and bss. */ for (i = 0; i < hdr->e_shnum; i++) shdr[i].sh_addr = 0; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_size == 0) continue; switch (shdr[i].sh_type) { case SHT_PROGBITS: case SHT_NOBITS: lastaddr = roundup(lastaddr, shdr[i].sh_addralign); shdr[i].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[i].sh_size; break; } } /* Symbols. */ nsym = 0; for (i = 0; i < hdr->e_shnum; i++) { switch (shdr[i].sh_type) { case SHT_SYMTAB: nsym++; ef->symtabindex = i; shdr[i].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[i].sh_size; break; } } if (nsym != 1) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: file has no valid symbol table\n"); goto out; } lastaddr = roundup(lastaddr, shdr[ef->symtabindex].sh_addralign); shdr[ef->symtabindex].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[ef->symtabindex].sh_size; symstrindex = shdr[ef->symtabindex].sh_link; if (symstrindex < 0 || symstrindex >= hdr->e_shnum || shdr[symstrindex].sh_type != SHT_STRTAB) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: file has invalid symbol strings\n"); goto out; } lastaddr = roundup(lastaddr, shdr[symstrindex].sh_addralign); shdr[symstrindex].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[symstrindex].sh_size; /* Section names. */ if (hdr->e_shstrndx == 0 || hdr->e_shstrndx >= hdr->e_shnum || shdr[hdr->e_shstrndx].sh_type != SHT_STRTAB) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: file has no section names\n"); goto out; } ef->shstrindex = hdr->e_shstrndx; lastaddr = roundup(lastaddr, shdr[ef->shstrindex].sh_addralign); shdr[ef->shstrindex].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[ef->shstrindex].sh_size; /* Relocation tables. */ for (i = 0; i < hdr->e_shnum; i++) { switch (shdr[i].sh_type) { case SHT_REL: case SHT_RELA: lastaddr = roundup(lastaddr, shdr[i].sh_addralign); shdr[i].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[i].sh_size; break; } } /* Clear the whole area, including bss regions. */ kern_bzero(firstaddr, lastaddr - firstaddr); /* Figure section with the lowest file offset we haven't loaded yet. */ for (cshdr = NULL; /* none */; /* none */) { /* * Find next section to load. The complexity of this loop is * O(n^2), but with the number of sections being typically * small, we do not care. */ lshdr = cshdr; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_addr == 0 || shdr[i].sh_type == SHT_NOBITS) continue; /* Skip sections that were loaded already. */ if (lshdr != NULL && lshdr->sh_offset >= shdr[i].sh_offset) continue; /* Find section with smallest offset. */ if (cshdr == lshdr || cshdr->sh_offset > shdr[i].sh_offset) cshdr = &shdr[i]; } if (cshdr == lshdr) break; if (kern_pread(ef->fd, (vm_offset_t)cshdr->sh_addr, cshdr->sh_size, (off_t)cshdr->sh_offset) != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: read failed\n"); goto out; } } file_addmetadata(fp, MODINFOMD_SHDR, shdrbytes, shdr); res = __elfN(obj_parse_modmetadata)(fp, ef); if (res != 0) goto out; ret = lastaddr - firstaddr; fp->f_addr = firstaddr; printf("size 0x%lx at 0x%lx", (u_long)ret, (u_long)firstaddr); out: printf("\n"); return ret; } #if defined(__i386__) && __ELF_WORD_SIZE == 64 struct mod_metadata64 { int md_version; /* structure version MDTV_* */ int md_type; /* type of entry MDT_* */ u_int64_t md_data; /* specific data */ u_int64_t md_cval; /* common string label */ }; #endif int __elfN(obj_parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef) { struct mod_metadata md; #if defined(__i386__) && __ELF_WORD_SIZE == 64 struct mod_metadata64 md64; #endif struct mod_depend *mdepend; struct mod_version mver; char *s; int error, modcnt, minfolen; Elf_Addr v, p, p_stop; if (__elfN(obj_lookup_set)(fp, ef, "modmetadata_set", &p, &p_stop, &modcnt) != 0) return 0; modcnt = 0; while (p < p_stop) { COPYOUT(p, &v, sizeof(v)); error = __elfN(obj_reloc_ptr)(fp, ef, p, &v, sizeof(v)); if (error != 0) return (error); #if defined(__i386__) && __ELF_WORD_SIZE == 64 COPYOUT(v, &md64, sizeof(md64)); error = __elfN(obj_reloc_ptr)(fp, ef, v, &md64, sizeof(md64)); if (error != 0) return (error); md.md_version = md64.md_version; md.md_type = md64.md_type; md.md_cval = (const char *)(uintptr_t)md64.md_cval; md.md_data = (void *)(uintptr_t)md64.md_data; #else COPYOUT(v, &md, sizeof(md)); error = __elfN(obj_reloc_ptr)(fp, ef, v, &md, sizeof(md)); if (error != 0) return (error); #endif p += sizeof(Elf_Addr); switch(md.md_type) { case MDT_DEPEND: s = strdupout((vm_offset_t)md.md_cval); minfolen = sizeof(*mdepend) + strlen(s) + 1; mdepend = malloc(minfolen); if (mdepend == NULL) return ENOMEM; COPYOUT((vm_offset_t)md.md_data, mdepend, sizeof(*mdepend)); strcpy((char*)(mdepend + 1), s); free(s); file_addmetadata(fp, MODINFOMD_DEPLIST, minfolen, mdepend); free(mdepend); break; case MDT_VERSION: s = strdupout((vm_offset_t)md.md_cval); COPYOUT((vm_offset_t)md.md_data, &mver, sizeof(mver)); file_addmodule(fp, s, mver.mv_version, NULL); free(s); modcnt++; break; case MDT_MODULE: + case MDT_PNP_INFO: break; default: printf("unknown type %d\n", md.md_type); break; } } return 0; } static int __elfN(obj_lookup_set)(struct preloaded_file *fp, elf_file_t ef, const char* name, Elf_Addr *startp, Elf_Addr *stopp, int *countp) { Elf_Ehdr *hdr; Elf_Shdr *shdr; char *p; vm_offset_t shstrtab; int i; hdr = &ef->hdr; shdr = ef->e_shdr; shstrtab = shdr[ef->shstrindex].sh_addr; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_type != SHT_PROGBITS) continue; if (shdr[i].sh_name == 0) continue; p = strdupout(shstrtab + shdr[i].sh_name); if (strncmp(p, "set_", 4) == 0 && strcmp(p + 4, name) == 0) { *startp = shdr[i].sh_addr; *stopp = shdr[i].sh_addr + shdr[i].sh_size; *countp = (*stopp - *startp) / sizeof(Elf_Addr); free(p); return (0); } free(p); } return (ESRCH); } /* * Apply any intra-module relocations to the value. p is the load address * of the value and val/len is the value to be modified. This does NOT modify * the image in-place, because this is done by kern_linker later on. */ static int __elfN(obj_reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len) { Elf_Ehdr *hdr; Elf_Shdr *shdr; Elf_Addr off = p; Elf_Addr base; Elf_Rela a, *abase; Elf_Rel r, *rbase; int error, i, j, nrel, nrela; hdr = &ef->hdr; shdr = ef->e_shdr; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_type != SHT_RELA && shdr[i].sh_type != SHT_REL) continue; base = shdr[shdr[i].sh_info].sh_addr; if (base == 0 || shdr[i].sh_addr == 0) continue; if (off < base || off + len > base + shdr[shdr[i].sh_info].sh_size) continue; switch (shdr[i].sh_type) { case SHT_RELA: abase = (Elf_Rela *)(intptr_t)shdr[i].sh_addr; nrela = shdr[i].sh_size / sizeof(Elf_Rela); for (j = 0; j < nrela; j++) { COPYOUT(abase + j, &a, sizeof(a)); error = __elfN(reloc)(ef, __elfN(obj_symaddr), &a, ELF_RELOC_RELA, base, off, val, len); if (error != 0) return (error); } break; case SHT_REL: rbase = (Elf_Rel *)(intptr_t)shdr[i].sh_addr; nrel = shdr[i].sh_size / sizeof(Elf_Rel); for (j = 0; j < nrel; j++) { COPYOUT(rbase + j, &r, sizeof(r)); error = __elfN(reloc)(ef, __elfN(obj_symaddr), &r, ELF_RELOC_REL, base, off, val, len); if (error != 0) return (error); } break; } } return (0); } /* Look up the address of a specified symbol. */ static Elf_Addr __elfN(obj_symaddr)(struct elf_file *ef, Elf_Size symidx) { Elf_Sym sym; Elf_Addr base; int symcnt; symcnt = ef->e_shdr[ef->symtabindex].sh_size / sizeof(Elf_Sym); if (symidx >= symcnt) return (0); COPYOUT(ef->e_shdr[ef->symtabindex].sh_addr + symidx * sizeof(Elf_Sym), &sym, sizeof(sym)); if (sym.st_shndx == SHN_UNDEF || sym.st_shndx >= ef->hdr.e_shnum) return (0); base = ef->e_shdr[sym.st_shndx].sh_addr; if (base == 0) return (0); return (base + sym.st_value); } Index: projects/ifnet/sys/boot/fdt/dts/arm/beaglebone-black.dts =================================================================== --- projects/ifnet/sys/boot/fdt/dts/arm/beaglebone-black.dts (revision 277209) +++ projects/ifnet/sys/boot/fdt/dts/arm/beaglebone-black.dts (revision 277210) @@ -1,185 +1,184 @@ /*- * Copyright (c) 2012 Damjan Marion * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /dts-v1/; /include/ "am335x.dtsi" / { model = "beaglebone-black"; compatible = "beaglebone-black", "beaglebone", "ti,am335x"; aliases { soc = &SOC; uart0 = &uart0; }; memory { device_type = "memory"; reg = < 0x80000000 0x20000000 >; /* 512MB RAM */ }; am335x { scm@44e10000 { /* Set of triplets < padname, muxname, padstate> */ scm-pad-config = /* I2C0 */ "I2C0_SDA", "I2C0_SDA","i2c", "I2C0_SCL", "I2C0_SCL","i2c", /* I2C1 */ "SPI0_D1", "I2C1_SDA", "i2c", "SPI0_CS0", "I2C1_SCL", "i2c", /* I2C2 */ "UART1_CTSn", "I2C2_SDA", "i2c", "UART1_RTSn", "I2C2_SCL", "i2c", /* Ethernet */ "MII1_RX_ER", "gmii1_rxerr", "input_pulldown", "MII1_TX_EN", "gmii1_txen", "output", "MII1_RX_DV", "gmii1_rxdv", "input_pulldown", "MII1_TXD3", "gmii1_txd3", "output", "MII1_TXD2", "gmii1_txd2", "output", "MII1_TXD1", "gmii1_txd1", "output", "MII1_TXD0", "gmii1_txd0", "output", "MII1_TX_CLK", "gmii1_txclk", "input_pulldown", "MII1_RX_CLK", "gmii1_rxclk", "input_pulldown", "MII1_RXD3", "gmii1_rxd3", "input_pulldown", "MII1_RXD2", "gmii1_rxd2", "input_pulldown", "MII1_RXD1", "gmii1_rxd1", "input_pulldown", "MII1_RXD0", "gmii1_rxd0", "input_pulldown", "MDIO", "mdio_data", "input_pullup", "MDC", "mdio_clk", "output_pullup", /* MMCSD0 */ "MMC0_CMD", "mmc0_cmd", "input_pullup", "MMC0_CLK", "mmc0_clk", "input_pullup", "MMC0_DAT0", "mmc0_dat0", "input_pullup", "MMC0_DAT1", "mmc0_dat1", "input_pullup", "MMC0_DAT2", "mmc0_dat2", "input_pullup", "MMC0_DAT3", "mmc0_dat3", "input_pullup", /* MMC1 */ "GPMC_CSn1", "mmc1_clk", "input_pullup", "GPMC_CSn2", "mmc1_cmd", "input_pullup", "GPMC_CSn3", "gpio2_0", "output_pullup", /* Reset */ "GPMC_AD0", "mmc1_dat0", "input_pullup", "GPMC_AD1", "mmc1_dat1", "input_pullup", "GPMC_AD2", "mmc1_dat2", "input_pullup", "GPMC_AD3", "mmc1_dat3", "input_pullup", "GPMC_AD4", "mmc1_dat4", "input_pullup", "GPMC_AD5", "mmc1_dat5", "input_pullup", "GPMC_AD6", "mmc1_dat6", "input_pullup", "GPMC_AD7", "mmc1_dat7", "input_pullup", /* GPIO */ "ECAP0_IN_PWM0_OUT", "gpio0_7", "input_pulldown", "GPMC_AD10", "gpio0_26", "input_pulldown", "GPMC_AD11", "gpio0_27", "input_pulldown", "GPMC_AD12", "gpio1_12", "input_pulldown", "GPMC_AD13", "gpio1_13", "input_pulldown", "GPMC_AD14", "gpio1_14", "input_pulldown", "GPMC_AD15", "gpio1_15", "input_pulldown", "GPMC_A0", "gpio1_16", "input_pulldown", "GPMC_A1", "gpio1_17", "input_pulldown", "GPMC_A5", "gpio1_21", "output", /* User LED 1 */ "GPMC_A6", "gpio1_22", "output", /* User LED 2 */ "GPMC_A7", "gpio1_23", "output", /* User LED 3 */ "GPMC_A8", "gpio1_24", "output", /* User LED 4 */ "GPMC_BEn1", "gpio1_28", "input_pulldown", "GPMC_CSn0", "gpio1_29", "input_pulldown", "GPMC_CLK", "gpio2_1", "input_pulldown", "LCD_DATA0", "gpio2_6", "input_pulldown", "LCD_DATA1", "gpio2_7", "input_pulldown", "LCD_DATA2", "gpio2_8", "input_pulldown", "LCD_DATA3", "gpio2_9", "input_pulldown", "LCD_DATA4", "gpio2_10", "input_pulldown", "LCD_DATA5", "gpio2_11", "input_pulldown", "LCD_DATA6", "gpio2_12", "input_pulldown", "LCD_DATA7", "gpio2_13", "input_pulldown", "LCD_VSYNC", "gpio2_22", "input_pulldown", "LCD_HSYNC", "gpio2_23", "input_pulldown", "LCD_PCLK", "gpio2_24", "input_pulldown", "LCD_AC_BIAS_EN", "gpio2_25", "input_pulldown", "MCASP0_FSR", "gpio3_19", "input_pulldown", "MCASP0_AHCLKX", "gpio3_21", "input_pulldown", /* TIMERs */ "GPMC_ADVn_ALE", "timer4", "output", "GPMC_BEn0_CLE", "timer5", "output", "GPMC_WEn", "timer6", "output", "GPMC_OEn_REn", "timer7", "output", /* USB0 and USB1 */ "USB0_DRVVBUS", "USB0_DRVVBUS", "output", "USB1_DRVVBUS", "USB1_DRVVBUS", "output", /* PWM */ "GPMC_A2", "ehrpwm1A", "output", "GPMC_A3", "ehrpwm1B", "output", "GPMC_AD8", "ehrpwm2A", "output", "GPMC_AD9", "ehrpwm2B", "output"; }; mmchs1@481D8000 { bus-width = <8>; status = "okay"; non-removable; }; - i2c@44e0b000 { - pmic@24 { + pmic@48 { compatible = "ti,am335x-pmic"; reg = <0x48>; }; }; }; leds { compatible = "gpio-leds"; led1 { gpios = <&GPIO 53 2 0>; name = "led1"; }; led2 { gpios = <&GPIO 54 2 0>; name = "led2"; }; led3 { gpios = <&GPIO 55 2 0>; name = "led3"; }; led4 { gpios = <&GPIO 56 2 0>; name = "led4"; }; }; chosen { stdin = "uart0"; stdout = "uart0"; }; }; Index: projects/ifnet/sys/boot =================================================================== --- projects/ifnet/sys/boot (revision 277209) +++ projects/ifnet/sys/boot (revision 277210) Property changes on: projects/ifnet/sys/boot ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys/boot:r277180-277209 Index: projects/ifnet/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/trim_map.c =================================================================== --- projects/ifnet/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/trim_map.c (revision 277209) +++ projects/ifnet/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/trim_map.c (revision 277210) @@ -1,636 +1,636 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2012 Pawel Jakub Dawidek . * All rights reserved. */ #include #include #include #include #include /* * Calculate the zio end, upgrading based on ashift which would be * done by zio_vdev_io_start. * * This makes free range consolidation much more effective * than it would otherwise be as well as ensuring that entire * blocks are invalidated by writes. */ #define TRIM_ZIO_END(vd, offset, size) (offset + \ P2ROUNDUP(size, 1ULL << vd->vdev_top->vdev_ashift)) /* Maximal segment size for ATA TRIM. */ #define TRIM_MAP_SIZE_FACTOR (512 << 16) #define TRIM_MAP_SEGS(size) (1 + (size) / TRIM_MAP_SIZE_FACTOR) #define TRIM_MAP_ADD(tm, ts) do { \ list_insert_tail(&(tm)->tm_head, (ts)); \ (tm)->tm_pending += TRIM_MAP_SEGS((ts)->ts_end - (ts)->ts_start); \ } while (0) #define TRIM_MAP_REM(tm, ts) do { \ list_remove(&(tm)->tm_head, (ts)); \ (tm)->tm_pending -= TRIM_MAP_SEGS((ts)->ts_end - (ts)->ts_start); \ } while (0) typedef struct trim_map { list_t tm_head; /* List of segments sorted by txg. */ avl_tree_t tm_queued_frees; /* AVL tree of segments waiting for TRIM. */ avl_tree_t tm_inflight_frees; /* AVL tree of in-flight TRIMs. */ avl_tree_t tm_inflight_writes; /* AVL tree of in-flight writes. */ list_t tm_pending_writes; /* Writes blocked on in-flight frees. */ kmutex_t tm_lock; uint64_t tm_pending; /* Count of pending TRIMs. */ } trim_map_t; typedef struct trim_seg { avl_node_t ts_node; /* AVL node. */ list_node_t ts_next; /* List element. */ uint64_t ts_start; /* Starting offset of this segment. */ uint64_t ts_end; /* Ending offset (non-inclusive). */ uint64_t ts_txg; /* Segment creation txg. */ hrtime_t ts_time; /* Segment creation time. */ } trim_seg_t; extern boolean_t zfs_trim_enabled; static u_int trim_txg_delay = 32; /* Keep deleted data up to 32 TXG */ static u_int trim_timeout = 30; /* Keep deleted data up to 30s */ static u_int trim_max_interval = 1; /* 1s delays between TRIMs */ static u_int trim_vdev_max_pending = 10000; /* Keep up to 10K segments */ SYSCTL_DECL(_vfs_zfs); SYSCTL_NODE(_vfs_zfs, OID_AUTO, trim, CTLFLAG_RD, 0, "ZFS TRIM"); SYSCTL_UINT(_vfs_zfs_trim, OID_AUTO, txg_delay, CTLFLAG_RWTUN, &trim_txg_delay, 0, "Delay TRIMs by up to this many TXGs"); SYSCTL_UINT(_vfs_zfs_trim, OID_AUTO, timeout, CTLFLAG_RWTUN, &trim_timeout, 0, "Delay TRIMs by up to this many seconds"); SYSCTL_UINT(_vfs_zfs_trim, OID_AUTO, max_interval, CTLFLAG_RWTUN, &trim_max_interval, 0, "Maximum interval between TRIM queue processing (seconds)"); SYSCTL_DECL(_vfs_zfs_vdev); SYSCTL_UINT(_vfs_zfs_vdev, OID_AUTO, trim_max_pending, CTLFLAG_RWTUN, &trim_vdev_max_pending, 0, "Maximum pending TRIM segments for a vdev"); static void trim_map_vdev_commit_done(spa_t *spa, vdev_t *vd); static int trim_map_seg_compare(const void *x1, const void *x2) { const trim_seg_t *s1 = x1; const trim_seg_t *s2 = x2; if (s1->ts_start < s2->ts_start) { if (s1->ts_end > s2->ts_start) return (0); return (-1); } if (s1->ts_start > s2->ts_start) { if (s1->ts_start < s2->ts_end) return (0); return (1); } return (0); } static int trim_map_zio_compare(const void *x1, const void *x2) { const zio_t *z1 = x1; const zio_t *z2 = x2; if (z1->io_offset < z2->io_offset) { if (z1->io_offset + z1->io_size > z2->io_offset) return (0); return (-1); } if (z1->io_offset > z2->io_offset) { if (z1->io_offset < z2->io_offset + z2->io_size) return (0); return (1); } return (0); } void trim_map_create(vdev_t *vd) { trim_map_t *tm; ASSERT(zfs_trim_enabled && !vd->vdev_notrim && vd->vdev_ops->vdev_op_leaf); tm = kmem_zalloc(sizeof (*tm), KM_SLEEP); mutex_init(&tm->tm_lock, NULL, MUTEX_DEFAULT, NULL); list_create(&tm->tm_head, sizeof (trim_seg_t), offsetof(trim_seg_t, ts_next)); list_create(&tm->tm_pending_writes, sizeof (zio_t), offsetof(zio_t, io_trim_link)); avl_create(&tm->tm_queued_frees, trim_map_seg_compare, sizeof (trim_seg_t), offsetof(trim_seg_t, ts_node)); avl_create(&tm->tm_inflight_frees, trim_map_seg_compare, sizeof (trim_seg_t), offsetof(trim_seg_t, ts_node)); avl_create(&tm->tm_inflight_writes, trim_map_zio_compare, sizeof (zio_t), offsetof(zio_t, io_trim_node)); vd->vdev_trimmap = tm; } void trim_map_destroy(vdev_t *vd) { trim_map_t *tm; trim_seg_t *ts; ASSERT(vd->vdev_ops->vdev_op_leaf); if (!zfs_trim_enabled) return; tm = vd->vdev_trimmap; if (tm == NULL) return; /* * We may have been called before trim_map_vdev_commit_done() * had a chance to run, so do it now to prune the remaining * inflight frees. */ trim_map_vdev_commit_done(vd->vdev_spa, vd); mutex_enter(&tm->tm_lock); while ((ts = list_head(&tm->tm_head)) != NULL) { avl_remove(&tm->tm_queued_frees, ts); TRIM_MAP_REM(tm, ts); kmem_free(ts, sizeof (*ts)); } mutex_exit(&tm->tm_lock); avl_destroy(&tm->tm_queued_frees); avl_destroy(&tm->tm_inflight_frees); avl_destroy(&tm->tm_inflight_writes); list_destroy(&tm->tm_pending_writes); list_destroy(&tm->tm_head); mutex_destroy(&tm->tm_lock); kmem_free(tm, sizeof (*tm)); vd->vdev_trimmap = NULL; } static void trim_map_segment_add(trim_map_t *tm, uint64_t start, uint64_t end, uint64_t txg) { avl_index_t where; trim_seg_t tsearch, *ts_before, *ts_after, *ts; boolean_t merge_before, merge_after; hrtime_t time; ASSERT(MUTEX_HELD(&tm->tm_lock)); VERIFY(start < end); time = gethrtime(); tsearch.ts_start = start; tsearch.ts_end = end; ts = avl_find(&tm->tm_queued_frees, &tsearch, &where); if (ts != NULL) { if (start < ts->ts_start) trim_map_segment_add(tm, start, ts->ts_start, txg); if (end > ts->ts_end) trim_map_segment_add(tm, ts->ts_end, end, txg); return; } ts_before = avl_nearest(&tm->tm_queued_frees, where, AVL_BEFORE); ts_after = avl_nearest(&tm->tm_queued_frees, where, AVL_AFTER); merge_before = (ts_before != NULL && ts_before->ts_end == start); merge_after = (ts_after != NULL && ts_after->ts_start == end); if (merge_before && merge_after) { avl_remove(&tm->tm_queued_frees, ts_before); TRIM_MAP_REM(tm, ts_before); TRIM_MAP_REM(tm, ts_after); ts_after->ts_start = ts_before->ts_start; ts_after->ts_txg = txg; ts_after->ts_time = time; TRIM_MAP_ADD(tm, ts_after); kmem_free(ts_before, sizeof (*ts_before)); } else if (merge_before) { TRIM_MAP_REM(tm, ts_before); ts_before->ts_end = end; ts_before->ts_txg = txg; ts_before->ts_time = time; TRIM_MAP_ADD(tm, ts_before); } else if (merge_after) { TRIM_MAP_REM(tm, ts_after); ts_after->ts_start = start; ts_after->ts_txg = txg; ts_after->ts_time = time; TRIM_MAP_ADD(tm, ts_after); } else { ts = kmem_alloc(sizeof (*ts), KM_SLEEP); ts->ts_start = start; ts->ts_end = end; ts->ts_txg = txg; ts->ts_time = time; avl_insert(&tm->tm_queued_frees, ts, where); TRIM_MAP_ADD(tm, ts); } } static void trim_map_segment_remove(trim_map_t *tm, trim_seg_t *ts, uint64_t start, uint64_t end) { trim_seg_t *nts; boolean_t left_over, right_over; ASSERT(MUTEX_HELD(&tm->tm_lock)); left_over = (ts->ts_start < start); right_over = (ts->ts_end > end); TRIM_MAP_REM(tm, ts); if (left_over && right_over) { nts = kmem_alloc(sizeof (*nts), KM_SLEEP); nts->ts_start = end; nts->ts_end = ts->ts_end; nts->ts_txg = ts->ts_txg; nts->ts_time = ts->ts_time; ts->ts_end = start; avl_insert_here(&tm->tm_queued_frees, nts, ts, AVL_AFTER); TRIM_MAP_ADD(tm, ts); TRIM_MAP_ADD(tm, nts); } else if (left_over) { ts->ts_end = start; TRIM_MAP_ADD(tm, ts); } else if (right_over) { ts->ts_start = end; TRIM_MAP_ADD(tm, ts); } else { avl_remove(&tm->tm_queued_frees, ts); kmem_free(ts, sizeof (*ts)); } } static void trim_map_free_locked(trim_map_t *tm, uint64_t start, uint64_t end, uint64_t txg) { zio_t zsearch, *zs; ASSERT(MUTEX_HELD(&tm->tm_lock)); zsearch.io_offset = start; zsearch.io_size = end - start; zs = avl_find(&tm->tm_inflight_writes, &zsearch, NULL); if (zs == NULL) { trim_map_segment_add(tm, start, end, txg); return; } if (start < zs->io_offset) trim_map_free_locked(tm, start, zs->io_offset, txg); if (zs->io_offset + zs->io_size < end) trim_map_free_locked(tm, zs->io_offset + zs->io_size, end, txg); } void trim_map_free(vdev_t *vd, uint64_t offset, uint64_t size, uint64_t txg) { trim_map_t *tm = vd->vdev_trimmap; if (!zfs_trim_enabled || vd->vdev_notrim || tm == NULL) return; mutex_enter(&tm->tm_lock); trim_map_free_locked(tm, offset, TRIM_ZIO_END(vd, offset, size), txg); mutex_exit(&tm->tm_lock); } boolean_t trim_map_write_start(zio_t *zio) { vdev_t *vd = zio->io_vd; trim_map_t *tm = vd->vdev_trimmap; trim_seg_t tsearch, *ts; boolean_t left_over, right_over; uint64_t start, end; if (!zfs_trim_enabled || vd->vdev_notrim || tm == NULL) return (B_TRUE); start = zio->io_offset; end = TRIM_ZIO_END(zio->io_vd, start, zio->io_size); tsearch.ts_start = start; tsearch.ts_end = end; mutex_enter(&tm->tm_lock); /* * Checking for colliding in-flight frees. */ ts = avl_find(&tm->tm_inflight_frees, &tsearch, NULL); if (ts != NULL) { list_insert_tail(&tm->tm_pending_writes, zio); mutex_exit(&tm->tm_lock); return (B_FALSE); } ts = avl_find(&tm->tm_queued_frees, &tsearch, NULL); if (ts != NULL) { /* * Loop until all overlapping segments are removed. */ do { trim_map_segment_remove(tm, ts, start, end); ts = avl_find(&tm->tm_queued_frees, &tsearch, NULL); } while (ts != NULL); } avl_add(&tm->tm_inflight_writes, zio); mutex_exit(&tm->tm_lock); return (B_TRUE); } void trim_map_write_done(zio_t *zio) { vdev_t *vd = zio->io_vd; trim_map_t *tm = vd->vdev_trimmap; /* * Don't check for vdev_notrim, since the write could have * started before vdev_notrim was set. */ if (!zfs_trim_enabled || tm == NULL) return; mutex_enter(&tm->tm_lock); /* * Don't fail if the write isn't in the tree, since the write * could have started after vdev_notrim was set. */ if (zio->io_trim_node.avl_child[0] || zio->io_trim_node.avl_child[1] || AVL_XPARENT(&zio->io_trim_node) || tm->tm_inflight_writes.avl_root == &zio->io_trim_node) avl_remove(&tm->tm_inflight_writes, zio); mutex_exit(&tm->tm_lock); } /* * Return the oldest segment (the one with the lowest txg / time) or NULL if: * 1. The list is empty * 2. The first element's txg is greater than txgsafe * 3. The first element's txg is not greater than the txg argument and the * the first element's time is not greater than time argument */ static trim_seg_t * trim_map_first(trim_map_t *tm, uint64_t txg, uint64_t txgsafe, hrtime_t time, boolean_t force) { trim_seg_t *ts; ASSERT(MUTEX_HELD(&tm->tm_lock)); VERIFY(txgsafe >= txg); ts = list_head(&tm->tm_head); if (ts != NULL && ts->ts_txg <= txgsafe && (ts->ts_txg <= txg || ts->ts_time <= time || force)) return (ts); return (NULL); } static void trim_map_vdev_commit(spa_t *spa, zio_t *zio, vdev_t *vd) { trim_map_t *tm = vd->vdev_trimmap; trim_seg_t *ts; uint64_t size, offset, txgtarget, txgsafe; int64_t hard, soft; hrtime_t timelimit; ASSERT(vd->vdev_ops->vdev_op_leaf); if (tm == NULL) return; - timelimit = gethrtime() - trim_timeout * NANOSEC; + timelimit = gethrtime() - (hrtime_t)trim_timeout * NANOSEC; if (vd->vdev_isl2cache) { txgsafe = UINT64_MAX; txgtarget = UINT64_MAX; } else { txgsafe = MIN(spa_last_synced_txg(spa), spa_freeze_txg(spa)); if (txgsafe > trim_txg_delay) txgtarget = txgsafe - trim_txg_delay; else txgtarget = 0; } mutex_enter(&tm->tm_lock); hard = 0; if (tm->tm_pending > trim_vdev_max_pending) hard = (tm->tm_pending - trim_vdev_max_pending) / 4; soft = P2ROUNDUP(hard + tm->tm_pending / trim_timeout + 1, 64); /* Loop until we have sent all outstanding free's */ while (soft > 0 && (ts = trim_map_first(tm, txgtarget, txgsafe, timelimit, hard > 0)) != NULL) { TRIM_MAP_REM(tm, ts); avl_remove(&tm->tm_queued_frees, ts); avl_add(&tm->tm_inflight_frees, ts); size = ts->ts_end - ts->ts_start; offset = ts->ts_start; /* * We drop the lock while we call zio_nowait as the IO * scheduler can result in a different IO being run e.g. * a write which would result in a recursive lock. */ mutex_exit(&tm->tm_lock); zio_nowait(zio_trim(zio, spa, vd, offset, size)); soft -= TRIM_MAP_SEGS(size); hard -= TRIM_MAP_SEGS(size); mutex_enter(&tm->tm_lock); } mutex_exit(&tm->tm_lock); } static void trim_map_vdev_commit_done(spa_t *spa, vdev_t *vd) { trim_map_t *tm = vd->vdev_trimmap; trim_seg_t *ts; list_t pending_writes; zio_t *zio; uint64_t start, size; void *cookie; ASSERT(vd->vdev_ops->vdev_op_leaf); if (tm == NULL) return; mutex_enter(&tm->tm_lock); if (!avl_is_empty(&tm->tm_inflight_frees)) { cookie = NULL; while ((ts = avl_destroy_nodes(&tm->tm_inflight_frees, &cookie)) != NULL) { kmem_free(ts, sizeof (*ts)); } } list_create(&pending_writes, sizeof (zio_t), offsetof(zio_t, io_trim_link)); list_move_tail(&pending_writes, &tm->tm_pending_writes); mutex_exit(&tm->tm_lock); while ((zio = list_remove_head(&pending_writes)) != NULL) { zio_vdev_io_reissue(zio); zio_execute(zio); } list_destroy(&pending_writes); } static void trim_map_commit(spa_t *spa, zio_t *zio, vdev_t *vd) { int c; if (vd == NULL) return; if (vd->vdev_ops->vdev_op_leaf) { trim_map_vdev_commit(spa, zio, vd); } else { for (c = 0; c < vd->vdev_children; c++) trim_map_commit(spa, zio, vd->vdev_child[c]); } } static void trim_map_commit_done(spa_t *spa, vdev_t *vd) { int c; if (vd == NULL) return; if (vd->vdev_ops->vdev_op_leaf) { trim_map_vdev_commit_done(spa, vd); } else { for (c = 0; c < vd->vdev_children; c++) trim_map_commit_done(spa, vd->vdev_child[c]); } } static void trim_thread(void *arg) { spa_t *spa = arg; zio_t *zio; #ifdef _KERNEL (void) snprintf(curthread->td_name, sizeof(curthread->td_name), "trim %s", spa_name(spa)); #endif for (;;) { mutex_enter(&spa->spa_trim_lock); if (spa->spa_trim_thread == NULL) { spa->spa_trim_thread = curthread; cv_signal(&spa->spa_trim_cv); mutex_exit(&spa->spa_trim_lock); thread_exit(); } (void) cv_timedwait(&spa->spa_trim_cv, &spa->spa_trim_lock, hz * trim_max_interval); mutex_exit(&spa->spa_trim_lock); zio = zio_root(spa, NULL, NULL, ZIO_FLAG_CANFAIL); spa_config_enter(spa, SCL_STATE, FTAG, RW_READER); trim_map_commit(spa, zio, spa->spa_root_vdev); (void) zio_wait(zio); trim_map_commit_done(spa, spa->spa_root_vdev); spa_config_exit(spa, SCL_STATE, FTAG); } } void trim_thread_create(spa_t *spa) { if (!zfs_trim_enabled) return; mutex_init(&spa->spa_trim_lock, NULL, MUTEX_DEFAULT, NULL); cv_init(&spa->spa_trim_cv, NULL, CV_DEFAULT, NULL); mutex_enter(&spa->spa_trim_lock); spa->spa_trim_thread = thread_create(NULL, 0, trim_thread, spa, 0, &p0, TS_RUN, minclsyspri); mutex_exit(&spa->spa_trim_lock); } void trim_thread_destroy(spa_t *spa) { if (!zfs_trim_enabled) return; if (spa->spa_trim_thread == NULL) return; mutex_enter(&spa->spa_trim_lock); /* Setting spa_trim_thread to NULL tells the thread to stop. */ spa->spa_trim_thread = NULL; cv_signal(&spa->spa_trim_cv); /* The thread will set it back to != NULL on exit. */ while (spa->spa_trim_thread == NULL) cv_wait(&spa->spa_trim_cv, &spa->spa_trim_lock); spa->spa_trim_thread = NULL; mutex_exit(&spa->spa_trim_lock); cv_destroy(&spa->spa_trim_cv); mutex_destroy(&spa->spa_trim_lock); } void trim_thread_wakeup(spa_t *spa) { if (!zfs_trim_enabled) return; if (spa->spa_trim_thread == NULL) return; mutex_enter(&spa->spa_trim_lock); cv_signal(&spa->spa_trim_cv); mutex_exit(&spa->spa_trim_lock); } Index: projects/ifnet/sys/cddl/contrib/opensolaris =================================================================== --- projects/ifnet/sys/cddl/contrib/opensolaris (revision 277209) +++ projects/ifnet/sys/cddl/contrib/opensolaris (revision 277210) Property changes on: projects/ifnet/sys/cddl/contrib/opensolaris ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys/cddl/contrib/opensolaris:r277180-277209 Index: projects/ifnet/sys/fs/devfs/devfs_devs.c =================================================================== --- projects/ifnet/sys/fs/devfs/devfs_devs.c (revision 277209) +++ projects/ifnet/sys/fs/devfs/devfs_devs.c (revision 277210) @@ -1,716 +1,722 @@ /*- * Copyright (c) 2000,2004 * Poul-Henning Kamp. 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. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vfsops.c 1.36 * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * The one true (but secret) list of active devices in the system. * Locked by dev_lock()/devmtx */ struct cdev_priv_list cdevp_list = TAILQ_HEAD_INITIALIZER(cdevp_list); struct unrhdr *devfs_inos; static MALLOC_DEFINE(M_DEVFS2, "DEVFS2", "DEVFS data 2"); static MALLOC_DEFINE(M_DEVFS3, "DEVFS3", "DEVFS data 3"); static MALLOC_DEFINE(M_CDEVP, "DEVFS1", "DEVFS cdev_priv storage"); static SYSCTL_NODE(_vfs, OID_AUTO, devfs, CTLFLAG_RW, 0, "DEVFS filesystem"); static unsigned devfs_generation; SYSCTL_UINT(_vfs_devfs, OID_AUTO, generation, CTLFLAG_RD, &devfs_generation, 0, "DEVFS generation number"); unsigned devfs_rule_depth = 1; SYSCTL_UINT(_vfs_devfs, OID_AUTO, rule_depth, CTLFLAG_RW, &devfs_rule_depth, 0, "Max depth of ruleset include"); /* * Helper sysctl for devname(3). We're given a dev_t and return the * name, if any, registered by the device driver. */ static int sysctl_devname(SYSCTL_HANDLER_ARGS) { int error; dev_t ud; struct cdev_priv *cdp; struct cdev *dev; error = SYSCTL_IN(req, &ud, sizeof (ud)); if (error) return (error); if (ud == NODEV) return (EINVAL); dev = NULL; dev_lock(); TAILQ_FOREACH(cdp, &cdevp_list, cdp_list) if (cdp->cdp_inode == ud) { dev = &cdp->cdp_c; dev_refl(dev); break; } dev_unlock(); if (dev == NULL) return (ENOENT); error = SYSCTL_OUT(req, dev->si_name, strlen(dev->si_name) + 1); dev_rel(dev); return (error); } SYSCTL_PROC(_kern, OID_AUTO, devname, CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_ANYBODY|CTLFLAG_MPSAFE, NULL, 0, sysctl_devname, "", "devname(3) handler"); SYSCTL_INT(_debug_sizeof, OID_AUTO, cdev, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, sizeof(struct cdev), "sizeof(struct cdev)"); SYSCTL_INT(_debug_sizeof, OID_AUTO, cdev_priv, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, sizeof(struct cdev_priv), "sizeof(struct cdev_priv)"); struct cdev * devfs_alloc(int flags) { struct cdev_priv *cdp; struct cdev *cdev; struct timespec ts; cdp = malloc(sizeof *cdp, M_CDEVP, M_ZERO | ((flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK)); if (cdp == NULL) return (NULL); cdp->cdp_dirents = &cdp->cdp_dirent0; cdp->cdp_dirent0 = NULL; cdp->cdp_maxdirent = 0; cdp->cdp_inode = 0; cdev = &cdp->cdp_c; LIST_INIT(&cdev->si_children); vfs_timestamp(&ts); cdev->si_atime = cdev->si_mtime = cdev->si_ctime = ts; cdev->si_cred = NULL; + /* + * Avoid race with dev_rel() by setting the initial + * reference count to 1. This last reference is taken + * by the destroy_dev() function. + */ + cdev->si_refcount = 1; return (cdev); } int devfs_dev_exists(const char *name) { struct cdev_priv *cdp; mtx_assert(&devmtx, MA_OWNED); TAILQ_FOREACH(cdp, &cdevp_list, cdp_list) { if ((cdp->cdp_flags & CDP_ACTIVE) == 0) continue; if (devfs_pathpath(cdp->cdp_c.si_name, name) != 0) return (1); if (devfs_pathpath(name, cdp->cdp_c.si_name) != 0) return (1); } if (devfs_dir_find(name) != 0) return (1); return (0); } void devfs_free(struct cdev *cdev) { struct cdev_priv *cdp; cdp = cdev2priv(cdev); if (cdev->si_cred != NULL) crfree(cdev->si_cred); devfs_free_cdp_inode(cdp->cdp_inode); if (cdp->cdp_maxdirent > 0) free(cdp->cdp_dirents, M_DEVFS2); free(cdp, M_CDEVP); } struct devfs_dirent * devfs_find(struct devfs_dirent *dd, const char *name, int namelen, int type) { struct devfs_dirent *de; TAILQ_FOREACH(de, &dd->de_dlist, de_list) { if (namelen != de->de_dirent->d_namlen) continue; if (type != 0 && type != de->de_dirent->d_type) continue; if (bcmp(name, de->de_dirent->d_name, namelen) != 0) continue; break; } KASSERT(de == NULL || (de->de_flags & DE_DOOMED) == 0, ("devfs_find: returning a doomed entry")); return (de); } struct devfs_dirent * devfs_newdirent(char *name, int namelen) { int i; struct devfs_dirent *de; struct dirent d; d.d_namlen = namelen; i = sizeof (*de) + GENERIC_DIRSIZ(&d); de = malloc(i, M_DEVFS3, M_WAITOK | M_ZERO); de->de_dirent = (struct dirent *)(de + 1); de->de_dirent->d_namlen = namelen; de->de_dirent->d_reclen = GENERIC_DIRSIZ(&d); bcopy(name, de->de_dirent->d_name, namelen); de->de_dirent->d_name[namelen] = '\0'; vfs_timestamp(&de->de_ctime); de->de_mtime = de->de_atime = de->de_ctime; de->de_links = 1; de->de_holdcnt = 1; #ifdef MAC mac_devfs_init(de); #endif return (de); } struct devfs_dirent * devfs_parent_dirent(struct devfs_dirent *de) { if (de->de_dirent->d_type != DT_DIR) return (de->de_dir); if (de->de_flags & (DE_DOT | DE_DOTDOT)) return (NULL); de = TAILQ_FIRST(&de->de_dlist); /* "." */ if (de == NULL) return (NULL); de = TAILQ_NEXT(de, de_list); /* ".." */ if (de == NULL) return (NULL); return (de->de_dir); } struct devfs_dirent * devfs_vmkdir(struct devfs_mount *dmp, char *name, int namelen, struct devfs_dirent *dotdot, u_int inode) { struct devfs_dirent *dd; struct devfs_dirent *de; /* Create the new directory */ dd = devfs_newdirent(name, namelen); TAILQ_INIT(&dd->de_dlist); dd->de_dirent->d_type = DT_DIR; dd->de_mode = 0555; dd->de_links = 2; dd->de_dir = dd; if (inode != 0) dd->de_inode = inode; else dd->de_inode = alloc_unr(devfs_inos); /* * "." and ".." are always the two first entries in the * de_dlist list. * * Create the "." entry in the new directory. */ de = devfs_newdirent(".", 1); de->de_dirent->d_type = DT_DIR; de->de_flags |= DE_DOT; TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list); de->de_dir = dd; /* Create the ".." entry in the new directory. */ de = devfs_newdirent("..", 2); de->de_dirent->d_type = DT_DIR; de->de_flags |= DE_DOTDOT; TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list); if (dotdot == NULL) { de->de_dir = dd; } else { de->de_dir = dotdot; sx_assert(&dmp->dm_lock, SX_XLOCKED); TAILQ_INSERT_TAIL(&dotdot->de_dlist, dd, de_list); dotdot->de_links++; devfs_rules_apply(dmp, dd); } #ifdef MAC mac_devfs_create_directory(dmp->dm_mount, name, namelen, dd); #endif return (dd); } void devfs_dirent_free(struct devfs_dirent *de) { free(de, M_DEVFS3); } /* * Removes a directory if it is empty. Also empty parent directories are * removed recursively. */ static void devfs_rmdir_empty(struct devfs_mount *dm, struct devfs_dirent *de) { struct devfs_dirent *dd, *de_dot, *de_dotdot; sx_assert(&dm->dm_lock, SX_XLOCKED); for (;;) { KASSERT(de->de_dirent->d_type == DT_DIR, ("devfs_rmdir_empty: de is not a directory")); if ((de->de_flags & DE_DOOMED) != 0 || de == dm->dm_rootdir) return; de_dot = TAILQ_FIRST(&de->de_dlist); KASSERT(de_dot != NULL, ("devfs_rmdir_empty: . missing")); de_dotdot = TAILQ_NEXT(de_dot, de_list); KASSERT(de_dotdot != NULL, ("devfs_rmdir_empty: .. missing")); /* Return if the directory is not empty. */ if (TAILQ_NEXT(de_dotdot, de_list) != NULL) return; dd = devfs_parent_dirent(de); KASSERT(dd != NULL, ("devfs_rmdir_empty: NULL dd")); TAILQ_REMOVE(&de->de_dlist, de_dot, de_list); TAILQ_REMOVE(&de->de_dlist, de_dotdot, de_list); TAILQ_REMOVE(&dd->de_dlist, de, de_list); DEVFS_DE_HOLD(dd); devfs_delete(dm, de, DEVFS_DEL_NORECURSE); devfs_delete(dm, de_dot, DEVFS_DEL_NORECURSE); devfs_delete(dm, de_dotdot, DEVFS_DEL_NORECURSE); if (DEVFS_DE_DROP(dd)) { devfs_dirent_free(dd); return; } de = dd; } } /* * The caller needs to hold the dm for the duration of the call since * dm->dm_lock may be temporary dropped. */ void devfs_delete(struct devfs_mount *dm, struct devfs_dirent *de, int flags) { struct devfs_dirent *dd; struct vnode *vp; KASSERT((de->de_flags & DE_DOOMED) == 0, ("devfs_delete doomed dirent")); de->de_flags |= DE_DOOMED; if ((flags & DEVFS_DEL_NORECURSE) == 0) { dd = devfs_parent_dirent(de); if (dd != NULL) DEVFS_DE_HOLD(dd); if (de->de_flags & DE_USER) { KASSERT(dd != NULL, ("devfs_delete: NULL dd")); devfs_dir_unref_de(dm, dd); } } else dd = NULL; mtx_lock(&devfs_de_interlock); vp = de->de_vnode; if (vp != NULL) { VI_LOCK(vp); mtx_unlock(&devfs_de_interlock); vholdl(vp); sx_unlock(&dm->dm_lock); if ((flags & DEVFS_DEL_VNLOCKED) == 0) vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK | LK_RETRY); else VI_UNLOCK(vp); vgone(vp); if ((flags & DEVFS_DEL_VNLOCKED) == 0) VOP_UNLOCK(vp, 0); vdrop(vp); sx_xlock(&dm->dm_lock); } else mtx_unlock(&devfs_de_interlock); if (de->de_symlink) { free(de->de_symlink, M_DEVFS); de->de_symlink = NULL; } #ifdef MAC mac_devfs_destroy(de); #endif if (de->de_inode > DEVFS_ROOTINO) { devfs_free_cdp_inode(de->de_inode); de->de_inode = 0; } if (DEVFS_DE_DROP(de)) devfs_dirent_free(de); if (dd != NULL) { if (DEVFS_DE_DROP(dd)) devfs_dirent_free(dd); else devfs_rmdir_empty(dm, dd); } } /* * Called on unmount. * Recursively removes the entire tree. * The caller needs to hold the dm for the duration of the call. */ static void devfs_purge(struct devfs_mount *dm, struct devfs_dirent *dd) { struct devfs_dirent *de; sx_assert(&dm->dm_lock, SX_XLOCKED); DEVFS_DE_HOLD(dd); for (;;) { /* * Use TAILQ_LAST() to remove "." and ".." last. * We might need ".." to resolve a path in * devfs_dir_unref_de(). */ de = TAILQ_LAST(&dd->de_dlist, devfs_dlist_head); if (de == NULL) break; TAILQ_REMOVE(&dd->de_dlist, de, de_list); if (de->de_flags & DE_USER) devfs_dir_unref_de(dm, dd); if (de->de_flags & (DE_DOT | DE_DOTDOT)) devfs_delete(dm, de, DEVFS_DEL_NORECURSE); else if (de->de_dirent->d_type == DT_DIR) devfs_purge(dm, de); else devfs_delete(dm, de, DEVFS_DEL_NORECURSE); } if (DEVFS_DE_DROP(dd)) devfs_dirent_free(dd); else if ((dd->de_flags & DE_DOOMED) == 0) devfs_delete(dm, dd, DEVFS_DEL_NORECURSE); } /* * Each cdev_priv has an array of pointers to devfs_dirent which is indexed * by the mount points dm_idx. * This function extends the array when necessary, taking into account that * the default array is 1 element and not malloc'ed. */ static void devfs_metoo(struct cdev_priv *cdp, struct devfs_mount *dm) { struct devfs_dirent **dep; int siz; siz = (dm->dm_idx + 1) * sizeof *dep; dep = malloc(siz, M_DEVFS2, M_WAITOK | M_ZERO); dev_lock(); if (dm->dm_idx <= cdp->cdp_maxdirent) { /* We got raced */ dev_unlock(); free(dep, M_DEVFS2); return; } memcpy(dep, cdp->cdp_dirents, (cdp->cdp_maxdirent + 1) * sizeof *dep); if (cdp->cdp_maxdirent > 0) free(cdp->cdp_dirents, M_DEVFS2); cdp->cdp_dirents = dep; /* * XXX: if malloc told us how much we actually got this could * XXX: be optimized. */ cdp->cdp_maxdirent = dm->dm_idx; dev_unlock(); } /* * The caller needs to hold the dm for the duration of the call. */ static int devfs_populate_loop(struct devfs_mount *dm, int cleanup) { struct cdev_priv *cdp; struct devfs_dirent *de; struct devfs_dirent *dd, *dt; struct cdev *pdev; int de_flags, depth, j; char *q, *s; sx_assert(&dm->dm_lock, SX_XLOCKED); dev_lock(); TAILQ_FOREACH(cdp, &cdevp_list, cdp_list) { KASSERT(cdp->cdp_dirents != NULL, ("NULL cdp_dirents")); /* * If we are unmounting, or the device has been destroyed, * clean up our dirent. */ if ((cleanup || !(cdp->cdp_flags & CDP_ACTIVE)) && dm->dm_idx <= cdp->cdp_maxdirent && cdp->cdp_dirents[dm->dm_idx] != NULL) { de = cdp->cdp_dirents[dm->dm_idx]; cdp->cdp_dirents[dm->dm_idx] = NULL; KASSERT(cdp == de->de_cdp, ("%s %d %s %p %p", __func__, __LINE__, cdp->cdp_c.si_name, cdp, de->de_cdp)); KASSERT(de->de_dir != NULL, ("Null de->de_dir")); dev_unlock(); TAILQ_REMOVE(&de->de_dir->de_dlist, de, de_list); de->de_cdp = NULL; de->de_inode = 0; devfs_delete(dm, de, 0); dev_lock(); cdp->cdp_inuse--; dev_unlock(); return (1); } /* * GC any lingering devices */ if (!(cdp->cdp_flags & CDP_ACTIVE)) { if (cdp->cdp_inuse > 0) continue; TAILQ_REMOVE(&cdevp_list, cdp, cdp_list); dev_unlock(); dev_rel(&cdp->cdp_c); return (1); } /* * Don't create any new dirents if we are unmounting */ if (cleanup) continue; KASSERT((cdp->cdp_flags & CDP_ACTIVE), ("Bogons, I tell ya'!")); if (dm->dm_idx <= cdp->cdp_maxdirent && cdp->cdp_dirents[dm->dm_idx] != NULL) { de = cdp->cdp_dirents[dm->dm_idx]; KASSERT(cdp == de->de_cdp, ("inconsistent cdp")); continue; } cdp->cdp_inuse++; dev_unlock(); if (dm->dm_idx > cdp->cdp_maxdirent) devfs_metoo(cdp, dm); dd = dm->dm_rootdir; s = cdp->cdp_c.si_name; for (;;) { for (q = s; *q != '/' && *q != '\0'; q++) continue; if (*q != '/') break; de = devfs_find(dd, s, q - s, 0); if (de == NULL) de = devfs_vmkdir(dm, s, q - s, dd, 0); else if (de->de_dirent->d_type == DT_LNK) { de = devfs_find(dd, s, q - s, DT_DIR); if (de == NULL) de = devfs_vmkdir(dm, s, q - s, dd, 0); de->de_flags |= DE_COVERED; } s = q + 1; dd = de; KASSERT(dd->de_dirent->d_type == DT_DIR && (dd->de_flags & (DE_DOT | DE_DOTDOT)) == 0, ("%s: invalid directory (si_name=%s)", __func__, cdp->cdp_c.si_name)); } de_flags = 0; de = devfs_find(dd, s, q - s, DT_LNK); if (de != NULL) de_flags |= DE_COVERED; de = devfs_newdirent(s, q - s); if (cdp->cdp_c.si_flags & SI_ALIAS) { de->de_uid = 0; de->de_gid = 0; de->de_mode = 0755; de->de_dirent->d_type = DT_LNK; pdev = cdp->cdp_c.si_parent; dt = dd; depth = 0; while (dt != dm->dm_rootdir && (dt = devfs_parent_dirent(dt)) != NULL) depth++; j = depth * 3 + strlen(pdev->si_name) + 1; de->de_symlink = malloc(j, M_DEVFS, M_WAITOK); de->de_symlink[0] = 0; while (depth-- > 0) strcat(de->de_symlink, "../"); strcat(de->de_symlink, pdev->si_name); } else { de->de_uid = cdp->cdp_c.si_uid; de->de_gid = cdp->cdp_c.si_gid; de->de_mode = cdp->cdp_c.si_mode; de->de_dirent->d_type = DT_CHR; } de->de_flags |= de_flags; de->de_inode = cdp->cdp_inode; de->de_cdp = cdp; #ifdef MAC mac_devfs_create_device(cdp->cdp_c.si_cred, dm->dm_mount, &cdp->cdp_c, de); #endif de->de_dir = dd; TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list); devfs_rules_apply(dm, de); dev_lock(); /* XXX: could check that cdp is still active here */ KASSERT(cdp->cdp_dirents[dm->dm_idx] == NULL, ("%s %d\n", __func__, __LINE__)); cdp->cdp_dirents[dm->dm_idx] = de; KASSERT(de->de_cdp != (void *)0xdeadc0de, ("%s %d\n", __func__, __LINE__)); dev_unlock(); return (1); } dev_unlock(); return (0); } /* * The caller needs to hold the dm for the duration of the call. */ void devfs_populate(struct devfs_mount *dm) { unsigned gen; sx_assert(&dm->dm_lock, SX_XLOCKED); gen = devfs_generation; if (dm->dm_generation == gen) return; while (devfs_populate_loop(dm, 0)) continue; dm->dm_generation = gen; } /* * The caller needs to hold the dm for the duration of the call. */ void devfs_cleanup(struct devfs_mount *dm) { sx_assert(&dm->dm_lock, SX_XLOCKED); while (devfs_populate_loop(dm, 1)) continue; devfs_purge(dm, dm->dm_rootdir); } /* * devfs_create() and devfs_destroy() are called from kern_conf.c and * in both cases the devlock() mutex is held, so no further locking * is necessary and no sleeping allowed. */ void devfs_create(struct cdev *dev) { struct cdev_priv *cdp; mtx_assert(&devmtx, MA_OWNED); cdp = cdev2priv(dev); cdp->cdp_flags |= CDP_ACTIVE; cdp->cdp_inode = alloc_unrl(devfs_inos); dev_refl(dev); TAILQ_INSERT_TAIL(&cdevp_list, cdp, cdp_list); devfs_generation++; } void devfs_destroy(struct cdev *dev) { struct cdev_priv *cdp; mtx_assert(&devmtx, MA_OWNED); cdp = cdev2priv(dev); cdp->cdp_flags &= ~CDP_ACTIVE; devfs_generation++; } ino_t devfs_alloc_cdp_inode(void) { return (alloc_unr(devfs_inos)); } void devfs_free_cdp_inode(ino_t ino) { if (ino > 0) free_unr(devfs_inos, ino); } static void devfs_devs_init(void *junk __unused) { devfs_inos = new_unrhdr(DEVFS_ROOTINO + 1, INT_MAX, &devmtx); } SYSINIT(devfs_devs, SI_SUB_DEVFS, SI_ORDER_FIRST, devfs_devs_init, NULL); Index: projects/ifnet/sys/kern/kern_conf.c =================================================================== --- projects/ifnet/sys/kern/kern_conf.c (revision 277209) +++ projects/ifnet/sys/kern/kern_conf.c (revision 277210) @@ -1,1475 +1,1473 @@ /*- * Copyright (c) 1999-2002 Poul-Henning Kamp * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_DEVT, "cdev", "cdev storage"); struct mtx devmtx; static void destroy_devl(struct cdev *dev); static int destroy_dev_sched_cbl(struct cdev *dev, void (*cb)(void *), void *arg); static void destroy_dev_tq(void *ctx, int pending); static int make_dev_credv(int flags, struct cdev **dres, struct cdevsw *devsw, int unit, struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, va_list ap); static struct cdev_priv_list cdevp_free_list = TAILQ_HEAD_INITIALIZER(cdevp_free_list); static SLIST_HEAD(free_cdevsw, cdevsw) cdevsw_gt_post_list = SLIST_HEAD_INITIALIZER(cdevsw_gt_post_list); void dev_lock(void) { mtx_lock(&devmtx); } /* * Free all the memory collected while the cdev mutex was * locked. Since devmtx is after the system map mutex, free() cannot * be called immediately and is postponed until cdev mutex can be * dropped. */ static void dev_unlock_and_free(void) { struct cdev_priv_list cdp_free; struct free_cdevsw csw_free; struct cdev_priv *cdp; struct cdevsw *csw; mtx_assert(&devmtx, MA_OWNED); /* * Make the local copy of the list heads while the dev_mtx is * held. Free it later. */ TAILQ_INIT(&cdp_free); TAILQ_CONCAT(&cdp_free, &cdevp_free_list, cdp_list); csw_free = cdevsw_gt_post_list; SLIST_INIT(&cdevsw_gt_post_list); mtx_unlock(&devmtx); while ((cdp = TAILQ_FIRST(&cdp_free)) != NULL) { TAILQ_REMOVE(&cdp_free, cdp, cdp_list); devfs_free(&cdp->cdp_c); } while ((csw = SLIST_FIRST(&csw_free)) != NULL) { SLIST_REMOVE_HEAD(&csw_free, d_postfree_list); free(csw, M_DEVT); } } static void dev_free_devlocked(struct cdev *cdev) { struct cdev_priv *cdp; mtx_assert(&devmtx, MA_OWNED); cdp = cdev2priv(cdev); TAILQ_INSERT_HEAD(&cdevp_free_list, cdp, cdp_list); } static void cdevsw_free_devlocked(struct cdevsw *csw) { mtx_assert(&devmtx, MA_OWNED); SLIST_INSERT_HEAD(&cdevsw_gt_post_list, csw, d_postfree_list); } void dev_unlock(void) { mtx_unlock(&devmtx); } void dev_ref(struct cdev *dev) { mtx_assert(&devmtx, MA_NOTOWNED); mtx_lock(&devmtx); dev->si_refcount++; mtx_unlock(&devmtx); } void dev_refl(struct cdev *dev) { mtx_assert(&devmtx, MA_OWNED); dev->si_refcount++; } void dev_rel(struct cdev *dev) { int flag = 0; mtx_assert(&devmtx, MA_NOTOWNED); dev_lock(); dev->si_refcount--; KASSERT(dev->si_refcount >= 0, ("dev_rel(%s) gave negative count", devtoname(dev))); #if 0 if (dev->si_usecount == 0 && (dev->si_flags & SI_CHEAPCLONE) && (dev->si_flags & SI_NAMED)) ; else #endif if (dev->si_devsw == NULL && dev->si_refcount == 0) { LIST_REMOVE(dev, si_list); flag = 1; } dev_unlock(); if (flag) devfs_free(dev); } struct cdevsw * dev_refthread(struct cdev *dev, int *ref) { struct cdevsw *csw; struct cdev_priv *cdp; mtx_assert(&devmtx, MA_NOTOWNED); if ((dev->si_flags & SI_ETERNAL) != 0) { *ref = 0; return (dev->si_devsw); } dev_lock(); csw = dev->si_devsw; if (csw != NULL) { cdp = cdev2priv(dev); if ((cdp->cdp_flags & CDP_SCHED_DTR) == 0) atomic_add_long(&dev->si_threadcount, 1); else csw = NULL; } dev_unlock(); *ref = 1; return (csw); } struct cdevsw * devvn_refthread(struct vnode *vp, struct cdev **devp, int *ref) { struct cdevsw *csw; struct cdev_priv *cdp; struct cdev *dev; mtx_assert(&devmtx, MA_NOTOWNED); if ((vp->v_vflag & VV_ETERNALDEV) != 0) { dev = vp->v_rdev; if (dev == NULL) return (NULL); KASSERT((dev->si_flags & SI_ETERNAL) != 0, ("Not eternal cdev")); *ref = 0; csw = dev->si_devsw; KASSERT(csw != NULL, ("Eternal cdev is destroyed")); *devp = dev; return (csw); } csw = NULL; dev_lock(); dev = vp->v_rdev; if (dev == NULL) { dev_unlock(); return (NULL); } cdp = cdev2priv(dev); if ((cdp->cdp_flags & CDP_SCHED_DTR) == 0) { csw = dev->si_devsw; if (csw != NULL) atomic_add_long(&dev->si_threadcount, 1); } dev_unlock(); if (csw != NULL) { *devp = dev; *ref = 1; } return (csw); } void dev_relthread(struct cdev *dev, int ref) { mtx_assert(&devmtx, MA_NOTOWNED); if (!ref) return; KASSERT(dev->si_threadcount > 0, ("%s threadcount is wrong", dev->si_name)); atomic_subtract_rel_long(&dev->si_threadcount, 1); } int nullop(void) { return (0); } int eopnotsupp(void) { return (EOPNOTSUPP); } static int enxio(void) { return (ENXIO); } static int enodev(void) { return (ENODEV); } /* Define a dead_cdevsw for use when devices leave unexpectedly. */ #define dead_open (d_open_t *)enxio #define dead_close (d_close_t *)enxio #define dead_read (d_read_t *)enxio #define dead_write (d_write_t *)enxio #define dead_ioctl (d_ioctl_t *)enxio #define dead_poll (d_poll_t *)enodev #define dead_mmap (d_mmap_t *)enodev static void dead_strategy(struct bio *bp) { biofinish(bp, NULL, ENXIO); } #define dead_dump (dumper_t *)enxio #define dead_kqfilter (d_kqfilter_t *)enxio #define dead_mmap_single (d_mmap_single_t *)enodev static struct cdevsw dead_cdevsw = { .d_version = D_VERSION, .d_open = dead_open, .d_close = dead_close, .d_read = dead_read, .d_write = dead_write, .d_ioctl = dead_ioctl, .d_poll = dead_poll, .d_mmap = dead_mmap, .d_strategy = dead_strategy, .d_name = "dead", .d_dump = dead_dump, .d_kqfilter = dead_kqfilter, .d_mmap_single = dead_mmap_single }; /* Default methods if driver does not specify method */ #define null_open (d_open_t *)nullop #define null_close (d_close_t *)nullop #define no_read (d_read_t *)enodev #define no_write (d_write_t *)enodev #define no_ioctl (d_ioctl_t *)enodev #define no_mmap (d_mmap_t *)enodev #define no_kqfilter (d_kqfilter_t *)enodev #define no_mmap_single (d_mmap_single_t *)enodev static void no_strategy(struct bio *bp) { biofinish(bp, NULL, ENODEV); } static int no_poll(struct cdev *dev __unused, int events, struct thread *td __unused) { return (poll_no_poll(events)); } #define no_dump (dumper_t *)enodev static int giant_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { struct cdevsw *dsw; int ref, retval; dsw = dev_refthread(dev, &ref); if (dsw == NULL) return (ENXIO); mtx_lock(&Giant); retval = dsw->d_gianttrick->d_open(dev, oflags, devtype, td); mtx_unlock(&Giant); dev_relthread(dev, ref); return (retval); } static int giant_fdopen(struct cdev *dev, int oflags, struct thread *td, struct file *fp) { struct cdevsw *dsw; int ref, retval; dsw = dev_refthread(dev, &ref); if (dsw == NULL) return (ENXIO); mtx_lock(&Giant); retval = dsw->d_gianttrick->d_fdopen(dev, oflags, td, fp); mtx_unlock(&Giant); dev_relthread(dev, ref); return (retval); } static int giant_close(struct cdev *dev, int fflag, int devtype, struct thread *td) { struct cdevsw *dsw; int ref, retval; dsw = dev_refthread(dev, &ref); if (dsw == NULL) return (ENXIO); mtx_lock(&Giant); retval = dsw->d_gianttrick->d_close(dev, fflag, devtype, td); mtx_unlock(&Giant); dev_relthread(dev, ref); return (retval); } static void giant_strategy(struct bio *bp) { struct cdevsw *dsw; struct cdev *dev; int ref; dev = bp->bio_dev; dsw = dev_refthread(dev, &ref); if (dsw == NULL) { biofinish(bp, NULL, ENXIO); return; } mtx_lock(&Giant); dsw->d_gianttrick->d_strategy(bp); mtx_unlock(&Giant); dev_relthread(dev, ref); } static int giant_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { struct cdevsw *dsw; int ref, retval; dsw = dev_refthread(dev, &ref); if (dsw == NULL) return (ENXIO); mtx_lock(&Giant); retval = dsw->d_gianttrick->d_ioctl(dev, cmd, data, fflag, td); mtx_unlock(&Giant); dev_relthread(dev, ref); return (retval); } static int giant_read(struct cdev *dev, struct uio *uio, int ioflag) { struct cdevsw *dsw; int ref, retval; dsw = dev_refthread(dev, &ref); if (dsw == NULL) return (ENXIO); mtx_lock(&Giant); retval = dsw->d_gianttrick->d_read(dev, uio, ioflag); mtx_unlock(&Giant); dev_relthread(dev, ref); return (retval); } static int giant_write(struct cdev *dev, struct uio *uio, int ioflag) { struct cdevsw *dsw; int ref, retval; dsw = dev_refthread(dev, &ref); if (dsw == NULL) return (ENXIO); mtx_lock(&Giant); retval = dsw->d_gianttrick->d_write(dev, uio, ioflag); mtx_unlock(&Giant); dev_relthread(dev, ref); return (retval); } static int giant_poll(struct cdev *dev, int events, struct thread *td) { struct cdevsw *dsw; int ref, retval; dsw = dev_refthread(dev, &ref); if (dsw == NULL) return (ENXIO); mtx_lock(&Giant); retval = dsw->d_gianttrick->d_poll(dev, events, td); mtx_unlock(&Giant); dev_relthread(dev, ref); return (retval); } static int giant_kqfilter(struct cdev *dev, struct knote *kn) { struct cdevsw *dsw; int ref, retval; dsw = dev_refthread(dev, &ref); if (dsw == NULL) return (ENXIO); mtx_lock(&Giant); retval = dsw->d_gianttrick->d_kqfilter(dev, kn); mtx_unlock(&Giant); dev_relthread(dev, ref); return (retval); } static int giant_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr) { struct cdevsw *dsw; int ref, retval; dsw = dev_refthread(dev, &ref); if (dsw == NULL) return (ENXIO); mtx_lock(&Giant); retval = dsw->d_gianttrick->d_mmap(dev, offset, paddr, nprot, memattr); mtx_unlock(&Giant); dev_relthread(dev, ref); return (retval); } static int giant_mmap_single(struct cdev *dev, vm_ooffset_t *offset, vm_size_t size, vm_object_t *object, int nprot) { struct cdevsw *dsw; int ref, retval; dsw = dev_refthread(dev, &ref); if (dsw == NULL) return (ENXIO); mtx_lock(&Giant); retval = dsw->d_gianttrick->d_mmap_single(dev, offset, size, object, nprot); mtx_unlock(&Giant); dev_relthread(dev, ref); return (retval); } static void notify(struct cdev *dev, const char *ev, int flags) { static const char prefix[] = "cdev="; char *data; int namelen, mflags; if (cold) return; mflags = (flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK; namelen = strlen(dev->si_name); data = malloc(namelen + sizeof(prefix), M_TEMP, mflags); if (data == NULL) return; memcpy(data, prefix, sizeof(prefix) - 1); memcpy(data + sizeof(prefix) - 1, dev->si_name, namelen + 1); devctl_notify_f("DEVFS", "CDEV", ev, data, mflags); free(data, M_TEMP); } static void notify_create(struct cdev *dev, int flags) { notify(dev, "CREATE", flags); } static void notify_destroy(struct cdev *dev) { notify(dev, "DESTROY", MAKEDEV_WAITOK); } static struct cdev * newdev(struct cdevsw *csw, int unit, struct cdev *si) { struct cdev *si2; mtx_assert(&devmtx, MA_OWNED); if (csw->d_flags & D_NEEDMINOR) { /* We may want to return an existing device */ LIST_FOREACH(si2, &csw->d_devs, si_list) { if (dev2unit(si2) == unit) { dev_free_devlocked(si); return (si2); } } } si->si_drv0 = unit; si->si_devsw = csw; LIST_INSERT_HEAD(&csw->d_devs, si, si_list); return (si); } static void fini_cdevsw(struct cdevsw *devsw) { struct cdevsw *gt; if (devsw->d_gianttrick != NULL) { gt = devsw->d_gianttrick; memcpy(devsw, gt, sizeof *devsw); cdevsw_free_devlocked(gt); devsw->d_gianttrick = NULL; } devsw->d_flags &= ~D_INIT; } static int prep_cdevsw(struct cdevsw *devsw, int flags) { struct cdevsw *dsw2; mtx_assert(&devmtx, MA_OWNED); if (devsw->d_flags & D_INIT) return (0); if (devsw->d_flags & D_NEEDGIANT) { dev_unlock(); dsw2 = malloc(sizeof *dsw2, M_DEVT, (flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK); dev_lock(); if (dsw2 == NULL && !(devsw->d_flags & D_INIT)) return (ENOMEM); } else dsw2 = NULL; if (devsw->d_flags & D_INIT) { if (dsw2 != NULL) cdevsw_free_devlocked(dsw2); return (0); } if (devsw->d_version != D_VERSION_03) { printf( "WARNING: Device driver \"%s\" has wrong version %s\n", devsw->d_name == NULL ? "???" : devsw->d_name, "and is disabled. Recompile KLD module."); devsw->d_open = dead_open; devsw->d_close = dead_close; devsw->d_read = dead_read; devsw->d_write = dead_write; devsw->d_ioctl = dead_ioctl; devsw->d_poll = dead_poll; devsw->d_mmap = dead_mmap; devsw->d_mmap_single = dead_mmap_single; devsw->d_strategy = dead_strategy; devsw->d_dump = dead_dump; devsw->d_kqfilter = dead_kqfilter; } if (devsw->d_flags & D_NEEDGIANT) { if (devsw->d_gianttrick == NULL) { memcpy(dsw2, devsw, sizeof *dsw2); devsw->d_gianttrick = dsw2; dsw2 = NULL; } } #define FIXUP(member, noop, giant) \ do { \ if (devsw->member == NULL) { \ devsw->member = noop; \ } else if (devsw->d_flags & D_NEEDGIANT) \ devsw->member = giant; \ } \ while (0) FIXUP(d_open, null_open, giant_open); FIXUP(d_fdopen, NULL, giant_fdopen); FIXUP(d_close, null_close, giant_close); FIXUP(d_read, no_read, giant_read); FIXUP(d_write, no_write, giant_write); FIXUP(d_ioctl, no_ioctl, giant_ioctl); FIXUP(d_poll, no_poll, giant_poll); FIXUP(d_mmap, no_mmap, giant_mmap); FIXUP(d_strategy, no_strategy, giant_strategy); FIXUP(d_kqfilter, no_kqfilter, giant_kqfilter); FIXUP(d_mmap_single, no_mmap_single, giant_mmap_single); if (devsw->d_dump == NULL) devsw->d_dump = no_dump; LIST_INIT(&devsw->d_devs); devsw->d_flags |= D_INIT; if (dsw2 != NULL) cdevsw_free_devlocked(dsw2); return (0); } static int prep_devname(struct cdev *dev, const char *fmt, va_list ap) { int len; char *from, *q, *s, *to; mtx_assert(&devmtx, MA_OWNED); len = vsnrprintf(dev->si_name, sizeof(dev->si_name), 32, fmt, ap); if (len > sizeof(dev->si_name) - 1) return (ENAMETOOLONG); /* Strip leading slashes. */ for (from = dev->si_name; *from == '/'; from++) ; for (to = dev->si_name; *from != '\0'; from++, to++) { /* * Spaces and double quotation marks cause * problems for the devctl(4) protocol. * Reject names containing those characters. */ if (isspace(*from) || *from == '"') return (EINVAL); /* Treat multiple sequential slashes as single. */ while (from[0] == '/' && from[1] == '/') from++; /* Trailing slash is considered invalid. */ if (from[0] == '/' && from[1] == '\0') return (EINVAL); *to = *from; } *to = '\0'; if (dev->si_name[0] == '\0') return (EINVAL); /* Disallow "." and ".." components. */ for (s = dev->si_name;;) { for (q = s; *q != '/' && *q != '\0'; q++) ; if (q - s == 1 && s[0] == '.') return (EINVAL); if (q - s == 2 && s[0] == '.' && s[1] == '.') return (EINVAL); if (*q != '/') break; s = q + 1; } if (devfs_dev_exists(dev->si_name) != 0) return (EEXIST); return (0); } static int make_dev_credv(int flags, struct cdev **dres, struct cdevsw *devsw, int unit, struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, va_list ap) { struct cdev *dev, *dev_new; int res; KASSERT((flags & MAKEDEV_WAITOK) == 0 || (flags & MAKEDEV_NOWAIT) == 0, ("make_dev_credv: both WAITOK and NOWAIT specified")); dev_new = devfs_alloc(flags); if (dev_new == NULL) return (ENOMEM); dev_lock(); res = prep_cdevsw(devsw, flags); if (res != 0) { dev_unlock(); devfs_free(dev_new); return (res); } dev = newdev(devsw, unit, dev_new); if ((dev->si_flags & SI_NAMED) == 0) { res = prep_devname(dev, fmt, ap); if (res != 0) { if ((flags & MAKEDEV_CHECKNAME) == 0) { panic( "make_dev_credv: bad si_name (error=%d, si_name=%s)", res, dev->si_name); } if (dev == dev_new) { LIST_REMOVE(dev, si_list); dev_unlock(); devfs_free(dev); } else dev_unlock(); return (res); } } if (flags & MAKEDEV_REF) dev_refl(dev); if (flags & MAKEDEV_ETERNAL) dev->si_flags |= SI_ETERNAL; if (dev->si_flags & SI_CHEAPCLONE && dev->si_flags & SI_NAMED) { /* * This is allowed as it removes races and generally * simplifies cloning devices. * XXX: still ?? */ dev_unlock_and_free(); *dres = dev; return (0); } KASSERT(!(dev->si_flags & SI_NAMED), ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)", devsw->d_name, dev2unit(dev), devtoname(dev))); dev->si_flags |= SI_NAMED; if (cr != NULL) dev->si_cred = crhold(cr); dev->si_uid = uid; dev->si_gid = gid; dev->si_mode = mode; devfs_create(dev); clean_unrhdrl(devfs_inos); dev_unlock_and_free(); notify_create(dev, flags); *dres = dev; return (0); } struct cdev * make_dev(struct cdevsw *devsw, int unit, uid_t uid, gid_t gid, int mode, const char *fmt, ...) { struct cdev *dev; va_list ap; int res; va_start(ap, fmt); res = make_dev_credv(0, &dev, devsw, unit, NULL, uid, gid, mode, fmt, ap); va_end(ap); KASSERT(res == 0 && dev != NULL, ("make_dev: failed make_dev_credv (error=%d)", res)); return (dev); } struct cdev * make_dev_cred(struct cdevsw *devsw, int unit, struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, ...) { struct cdev *dev; va_list ap; int res; va_start(ap, fmt); res = make_dev_credv(0, &dev, devsw, unit, cr, uid, gid, mode, fmt, ap); va_end(ap); KASSERT(res == 0 && dev != NULL, ("make_dev_cred: failed make_dev_credv (error=%d)", res)); return (dev); } struct cdev * make_dev_credf(int flags, struct cdevsw *devsw, int unit, struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, ...) { struct cdev *dev; va_list ap; int res; va_start(ap, fmt); res = make_dev_credv(flags, &dev, devsw, unit, cr, uid, gid, mode, fmt, ap); va_end(ap); KASSERT(((flags & MAKEDEV_NOWAIT) != 0 && res == ENOMEM) || ((flags & MAKEDEV_CHECKNAME) != 0 && res != ENOMEM) || res == 0, ("make_dev_credf: failed make_dev_credv (error=%d)", res)); return (res == 0 ? dev : NULL); } int make_dev_p(int flags, struct cdev **cdev, struct cdevsw *devsw, struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt, ...) { va_list ap; int res; va_start(ap, fmt); res = make_dev_credv(flags, cdev, devsw, 0, cr, uid, gid, mode, fmt, ap); va_end(ap); KASSERT(((flags & MAKEDEV_NOWAIT) != 0 && res == ENOMEM) || ((flags & MAKEDEV_CHECKNAME) != 0 && res != ENOMEM) || res == 0, ("make_dev_p: failed make_dev_credv (error=%d)", res)); return (res); } static void dev_dependsl(struct cdev *pdev, struct cdev *cdev) { cdev->si_parent = pdev; cdev->si_flags |= SI_CHILD; LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings); } void dev_depends(struct cdev *pdev, struct cdev *cdev) { dev_lock(); dev_dependsl(pdev, cdev); dev_unlock(); } static int make_dev_alias_v(int flags, struct cdev **cdev, struct cdev *pdev, const char *fmt, va_list ap) { struct cdev *dev; int error; KASSERT(pdev != NULL, ("make_dev_alias_v: pdev is NULL")); KASSERT((flags & MAKEDEV_WAITOK) == 0 || (flags & MAKEDEV_NOWAIT) == 0, ("make_dev_alias_v: both WAITOK and NOWAIT specified")); KASSERT((flags & ~(MAKEDEV_WAITOK | MAKEDEV_NOWAIT | MAKEDEV_CHECKNAME)) == 0, ("make_dev_alias_v: invalid flags specified (flags=%02x)", flags)); dev = devfs_alloc(flags); if (dev == NULL) return (ENOMEM); dev_lock(); dev->si_flags |= SI_ALIAS; error = prep_devname(dev, fmt, ap); if (error != 0) { if ((flags & MAKEDEV_CHECKNAME) == 0) { panic("make_dev_alias_v: bad si_name " "(error=%d, si_name=%s)", error, dev->si_name); } dev_unlock(); devfs_free(dev); return (error); } dev->si_flags |= SI_NAMED; devfs_create(dev); dev_dependsl(pdev, dev); clean_unrhdrl(devfs_inos); dev_unlock(); notify_create(dev, flags); *cdev = dev; return (0); } struct cdev * make_dev_alias(struct cdev *pdev, const char *fmt, ...) { struct cdev *dev; va_list ap; int res; va_start(ap, fmt); res = make_dev_alias_v(MAKEDEV_WAITOK, &dev, pdev, fmt, ap); va_end(ap); KASSERT(res == 0 && dev != NULL, ("make_dev_alias: failed make_dev_alias_v (error=%d)", res)); return (dev); } int make_dev_alias_p(int flags, struct cdev **cdev, struct cdev *pdev, const char *fmt, ...) { va_list ap; int res; va_start(ap, fmt); res = make_dev_alias_v(flags, cdev, pdev, fmt, ap); va_end(ap); return (res); } int make_dev_physpath_alias(int flags, struct cdev **cdev, struct cdev *pdev, struct cdev *old_alias, const char *physpath) { char *devfspath; int physpath_len; int max_parentpath_len; int parentpath_len; int devfspathbuf_len; int mflags; int ret; *cdev = NULL; devfspath = NULL; physpath_len = strlen(physpath); ret = EINVAL; if (physpath_len == 0) goto out; if (strncmp("id1,", physpath, 4) == 0) { physpath += 4; physpath_len -= 4; if (physpath_len == 0) goto out; } max_parentpath_len = SPECNAMELEN - physpath_len - /*/*/1; parentpath_len = strlen(pdev->si_name); if (max_parentpath_len < parentpath_len) { if (bootverbose) printf("WARNING: Unable to alias %s " "to %s/%s - path too long\n", pdev->si_name, physpath, pdev->si_name); ret = ENAMETOOLONG; goto out; } mflags = (flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK; devfspathbuf_len = physpath_len + /*/*/1 + parentpath_len + /*NUL*/1; devfspath = malloc(devfspathbuf_len, M_DEVBUF, mflags); if (devfspath == NULL) { ret = ENOMEM; goto out; } sprintf(devfspath, "%s/%s", physpath, pdev->si_name); if (old_alias != NULL && strcmp(old_alias->si_name, devfspath) == 0) { /* Retain the existing alias. */ *cdev = old_alias; old_alias = NULL; ret = 0; } else { ret = make_dev_alias_p(flags, cdev, pdev, "%s", devfspath); } out: if (old_alias != NULL) destroy_dev(old_alias); if (devfspath != NULL) free(devfspath, M_DEVBUF); return (ret); } static void destroy_devl(struct cdev *dev) { struct cdevsw *csw; struct cdev_privdata *p; mtx_assert(&devmtx, MA_OWNED); KASSERT(dev->si_flags & SI_NAMED, ("WARNING: Driver mistake: destroy_dev on %d\n", dev2unit(dev))); KASSERT((dev->si_flags & SI_ETERNAL) == 0, ("WARNING: Driver mistake: destroy_dev on eternal %d\n", dev2unit(dev))); devfs_destroy(dev); /* Remove name marking */ dev->si_flags &= ~SI_NAMED; - dev->si_refcount++; /* Avoid race with dev_rel() */ - /* If we are a child, remove us from the parents list */ if (dev->si_flags & SI_CHILD) { LIST_REMOVE(dev, si_siblings); dev->si_flags &= ~SI_CHILD; } /* Kill our children */ while (!LIST_EMPTY(&dev->si_children)) destroy_devl(LIST_FIRST(&dev->si_children)); /* Remove from clone list */ if (dev->si_flags & SI_CLONELIST) { LIST_REMOVE(dev, si_clone); dev->si_flags &= ~SI_CLONELIST; } csw = dev->si_devsw; dev->si_devsw = NULL; /* already NULL for SI_ALIAS */ while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) { csw->d_purge(dev); msleep(csw, &devmtx, PRIBIO, "devprg", hz/10); if (dev->si_threadcount) printf("Still %lu threads in %s\n", dev->si_threadcount, devtoname(dev)); } while (dev->si_threadcount != 0) { /* Use unique dummy wait ident */ msleep(&csw, &devmtx, PRIBIO, "devdrn", hz / 10); } dev_unlock(); notify_destroy(dev); mtx_lock(&cdevpriv_mtx); while ((p = LIST_FIRST(&cdev2priv(dev)->cdp_fdpriv)) != NULL) { devfs_destroy_cdevpriv(p); mtx_lock(&cdevpriv_mtx); } mtx_unlock(&cdevpriv_mtx); dev_lock(); dev->si_drv1 = 0; dev->si_drv2 = 0; bzero(&dev->__si_u, sizeof(dev->__si_u)); if (!(dev->si_flags & SI_ALIAS)) { /* Remove from cdevsw list */ LIST_REMOVE(dev, si_list); /* If cdevsw has no more struct cdev *'s, clean it */ if (LIST_EMPTY(&csw->d_devs)) { fini_cdevsw(csw); wakeup(&csw->d_devs); } } dev->si_flags &= ~SI_ALIAS; dev->si_refcount--; /* Avoid race with dev_rel() */ if (dev->si_refcount > 0) { LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list); } else { dev_free_devlocked(dev); } } static void delist_dev_locked(struct cdev *dev) { struct cdev *child; devfs_destroy(dev); LIST_FOREACH(child, &dev->si_children, si_siblings) delist_dev_locked(child); } void delist_dev(struct cdev *dev) { dev_lock(); delist_dev_locked(dev); dev_unlock(); } void destroy_dev(struct cdev *dev) { WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "destroy_dev"); dev_lock(); destroy_devl(dev); dev_unlock_and_free(); } const char * devtoname(struct cdev *dev) { return (dev->si_name); } int dev_stdclone(char *name, char **namep, const char *stem, int *unit) { int u, i; i = strlen(stem); if (bcmp(stem, name, i) != 0) return (0); if (!isdigit(name[i])) return (0); u = 0; if (name[i] == '0' && isdigit(name[i+1])) return (0); while (isdigit(name[i])) { u *= 10; u += name[i++] - '0'; } if (u > 0xffffff) return (0); *unit = u; if (namep) *namep = &name[i]; if (name[i]) return (2); return (1); } /* * Helper functions for cloning device drivers. * * The objective here is to make it unnecessary for the device drivers to * use rman or similar to manage their unit number space. Due to the way * we do "on-demand" devices, using rman or other "private" methods * will be very tricky to lock down properly once we lock down this file. * * Instead we give the drivers these routines which puts the struct cdev *'s * that are to be managed on their own list, and gives the driver the ability * to ask for the first free unit number or a given specified unit number. * * In addition these routines support paired devices (pty, nmdm and similar) * by respecting a number of "flag" bits in the minor number. * */ struct clonedevs { LIST_HEAD(,cdev) head; }; void clone_setup(struct clonedevs **cdp) { *cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO); LIST_INIT(&(*cdp)->head); } int clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **dp, int extra) { struct clonedevs *cd; struct cdev *dev, *ndev, *dl, *de; int unit, low, u; KASSERT(*cdp != NULL, ("clone_setup() not called in driver \"%s\"", csw->d_name)); KASSERT(!(extra & CLONE_UNITMASK), ("Illegal extra bits (0x%x) in clone_create", extra)); KASSERT(*up <= CLONE_UNITMASK, ("Too high unit (0x%x) in clone_create", *up)); KASSERT(csw->d_flags & D_NEEDMINOR, ("clone_create() on cdevsw without minor numbers")); /* * Search the list for a lot of things in one go: * A preexisting match is returned immediately. * The lowest free unit number if we are passed -1, and the place * in the list where we should insert that new element. * The place to insert a specified unit number, if applicable * the end of the list. */ unit = *up; ndev = devfs_alloc(MAKEDEV_WAITOK); dev_lock(); prep_cdevsw(csw, MAKEDEV_WAITOK); low = extra; de = dl = NULL; cd = *cdp; LIST_FOREACH(dev, &cd->head, si_clone) { KASSERT(dev->si_flags & SI_CLONELIST, ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); u = dev2unit(dev); if (u == (unit | extra)) { *dp = dev; dev_unlock(); devfs_free(ndev); return (0); } if (unit == -1 && u == low) { low++; de = dev; continue; } else if (u < (unit | extra)) { de = dev; continue; } else if (u > (unit | extra)) { dl = dev; break; } } if (unit == -1) unit = low & CLONE_UNITMASK; dev = newdev(csw, unit | extra, ndev); if (dev->si_flags & SI_CLONELIST) { printf("dev %p (%s) is on clonelist\n", dev, dev->si_name); printf("unit=%d, low=%d, extra=0x%x\n", unit, low, extra); LIST_FOREACH(dev, &cd->head, si_clone) { printf("\t%p %s\n", dev, dev->si_name); } panic("foo"); } KASSERT(!(dev->si_flags & SI_CLONELIST), ("Dev %p(%s) should not be on clonelist", dev, dev->si_name)); if (dl != NULL) LIST_INSERT_BEFORE(dl, dev, si_clone); else if (de != NULL) LIST_INSERT_AFTER(de, dev, si_clone); else LIST_INSERT_HEAD(&cd->head, dev, si_clone); dev->si_flags |= SI_CLONELIST; *up = unit; dev_unlock_and_free(); return (1); } /* * Kill everything still on the list. The driver should already have * disposed of any softc hung of the struct cdev *'s at this time. */ void clone_cleanup(struct clonedevs **cdp) { struct cdev *dev; struct cdev_priv *cp; struct clonedevs *cd; cd = *cdp; if (cd == NULL) return; dev_lock(); while (!LIST_EMPTY(&cd->head)) { dev = LIST_FIRST(&cd->head); LIST_REMOVE(dev, si_clone); KASSERT(dev->si_flags & SI_CLONELIST, ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); dev->si_flags &= ~SI_CLONELIST; cp = cdev2priv(dev); if (!(cp->cdp_flags & CDP_SCHED_DTR)) { cp->cdp_flags |= CDP_SCHED_DTR; KASSERT(dev->si_flags & SI_NAMED, ("Driver has goofed in cloning underways udev %jx unit %x", (uintmax_t)dev2udev(dev), dev2unit(dev))); destroy_devl(dev); } } dev_unlock_and_free(); free(cd, M_DEVBUF); *cdp = NULL; } static TAILQ_HEAD(, cdev_priv) dev_ddtr = TAILQ_HEAD_INITIALIZER(dev_ddtr); static struct task dev_dtr_task = TASK_INITIALIZER(0, destroy_dev_tq, NULL); static void destroy_dev_tq(void *ctx, int pending) { struct cdev_priv *cp; struct cdev *dev; void (*cb)(void *); void *cb_arg; dev_lock(); while (!TAILQ_EMPTY(&dev_ddtr)) { cp = TAILQ_FIRST(&dev_ddtr); dev = &cp->cdp_c; KASSERT(cp->cdp_flags & CDP_SCHED_DTR, ("cdev %p in dev_destroy_tq without CDP_SCHED_DTR", cp)); TAILQ_REMOVE(&dev_ddtr, cp, cdp_dtr_list); cb = cp->cdp_dtr_cb; cb_arg = cp->cdp_dtr_cb_arg; destroy_devl(dev); dev_unlock_and_free(); dev_rel(dev); if (cb != NULL) cb(cb_arg); dev_lock(); } dev_unlock(); } /* * devmtx shall be locked on entry. devmtx will be unlocked after * function return. */ static int destroy_dev_sched_cbl(struct cdev *dev, void (*cb)(void *), void *arg) { struct cdev_priv *cp; mtx_assert(&devmtx, MA_OWNED); cp = cdev2priv(dev); if (cp->cdp_flags & CDP_SCHED_DTR) { dev_unlock(); return (0); } dev_refl(dev); cp->cdp_flags |= CDP_SCHED_DTR; cp->cdp_dtr_cb = cb; cp->cdp_dtr_cb_arg = arg; TAILQ_INSERT_TAIL(&dev_ddtr, cp, cdp_dtr_list); dev_unlock(); taskqueue_enqueue(taskqueue_swi_giant, &dev_dtr_task); return (1); } int destroy_dev_sched_cb(struct cdev *dev, void (*cb)(void *), void *arg) { dev_lock(); return (destroy_dev_sched_cbl(dev, cb, arg)); } int destroy_dev_sched(struct cdev *dev) { return (destroy_dev_sched_cb(dev, NULL, NULL)); } void destroy_dev_drain(struct cdevsw *csw) { dev_lock(); while (!LIST_EMPTY(&csw->d_devs)) { msleep(&csw->d_devs, &devmtx, PRIBIO, "devscd", hz/10); } dev_unlock(); } void drain_dev_clone_events(void) { sx_xlock(&clone_drain_lock); sx_xunlock(&clone_drain_lock); } #include "opt_ddb.h" #ifdef DDB #include #include DB_SHOW_COMMAND(cdev, db_show_cdev) { struct cdev_priv *cdp; struct cdev *dev; u_int flags; char buf[512]; if (!have_addr) { TAILQ_FOREACH(cdp, &cdevp_list, cdp_list) { dev = &cdp->cdp_c; db_printf("%s %p\n", dev->si_name, dev); if (db_pager_quit) break; } return; } dev = (struct cdev *)addr; cdp = cdev2priv(dev); db_printf("dev %s ref %d use %ld thr %ld inuse %u fdpriv %p\n", dev->si_name, dev->si_refcount, dev->si_usecount, dev->si_threadcount, cdp->cdp_inuse, cdp->cdp_fdpriv.lh_first); db_printf("devsw %p si_drv0 %d si_drv1 %p si_drv2 %p\n", dev->si_devsw, dev->si_drv0, dev->si_drv1, dev->si_drv2); flags = dev->si_flags; #define SI_FLAG(flag) do { \ if (flags & (flag)) { \ if (buf[0] != '\0') \ strlcat(buf, ", ", sizeof(buf)); \ strlcat(buf, (#flag) + 3, sizeof(buf)); \ flags &= ~(flag); \ } \ } while (0) buf[0] = '\0'; SI_FLAG(SI_ETERNAL); SI_FLAG(SI_ALIAS); SI_FLAG(SI_NAMED); SI_FLAG(SI_CHEAPCLONE); SI_FLAG(SI_CHILD); SI_FLAG(SI_DUMPDEV); SI_FLAG(SI_CLONELIST); db_printf("si_flags %s\n", buf); flags = cdp->cdp_flags; #define CDP_FLAG(flag) do { \ if (flags & (flag)) { \ if (buf[0] != '\0') \ strlcat(buf, ", ", sizeof(buf)); \ strlcat(buf, (#flag) + 4, sizeof(buf)); \ flags &= ~(flag); \ } \ } while (0) buf[0] = '\0'; CDP_FLAG(CDP_ACTIVE); CDP_FLAG(CDP_SCHED_DTR); db_printf("cdp_flags %s\n", buf); } #endif Index: projects/ifnet/sys/kern/uipc_mbuf.c =================================================================== --- projects/ifnet/sys/kern/uipc_mbuf.c (revision 277209) +++ projects/ifnet/sys/kern/uipc_mbuf.c (revision 277210) @@ -1,2011 +1,2038 @@ /*- * Copyright (c) 1982, 1986, 1988, 1991, 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_param.h" #include "opt_mbuf_stress_test.h" #include "opt_mbuf_profiling.h" #include #include #include #include #include #include #include #include #include #include #include int max_linkhdr; int max_protohdr; int max_hdr; int max_datalen; #ifdef MBUF_STRESS_TEST int m_defragpackets; int m_defragbytes; int m_defraguseless; int m_defragfailure; int m_defragrandomfailures; #endif /* * sysctl(8) exported objects */ SYSCTL_INT(_kern_ipc, KIPC_MAX_LINKHDR, max_linkhdr, CTLFLAG_RD, &max_linkhdr, 0, "Size of largest link layer header"); SYSCTL_INT(_kern_ipc, KIPC_MAX_PROTOHDR, max_protohdr, CTLFLAG_RD, &max_protohdr, 0, "Size of largest protocol layer header"); SYSCTL_INT(_kern_ipc, KIPC_MAX_HDR, max_hdr, CTLFLAG_RD, &max_hdr, 0, "Size of largest link plus protocol header"); SYSCTL_INT(_kern_ipc, KIPC_MAX_DATALEN, max_datalen, CTLFLAG_RD, &max_datalen, 0, "Minimum space left in mbuf after max_hdr"); #ifdef MBUF_STRESS_TEST SYSCTL_INT(_kern_ipc, OID_AUTO, m_defragpackets, CTLFLAG_RD, &m_defragpackets, 0, ""); SYSCTL_INT(_kern_ipc, OID_AUTO, m_defragbytes, CTLFLAG_RD, &m_defragbytes, 0, ""); SYSCTL_INT(_kern_ipc, OID_AUTO, m_defraguseless, CTLFLAG_RD, &m_defraguseless, 0, ""); SYSCTL_INT(_kern_ipc, OID_AUTO, m_defragfailure, CTLFLAG_RD, &m_defragfailure, 0, ""); SYSCTL_INT(_kern_ipc, OID_AUTO, m_defragrandomfailures, CTLFLAG_RW, &m_defragrandomfailures, 0, ""); #endif /* * Ensure the correct size of various mbuf parameters. It could be off due * to compiler-induced padding and alignment artifacts. */ -CTASSERT(sizeof(struct mbuf) == MSIZE); CTASSERT(MSIZE - offsetof(struct mbuf, m_dat) == MLEN); CTASSERT(MSIZE - offsetof(struct mbuf, m_pktdat) == MHLEN); + +/* + * mbuf data storage should be 64-bit aligned regardless of architectural + * pointer size; check this is the case with and without a packet header. + */ +CTASSERT(offsetof(struct mbuf, m_dat) % 8 == 0); +CTASSERT(offsetof(struct mbuf, m_pktdat) % 8 == 0); + +/* + * While the specific values here don't matter too much (i.e., +/- a few + * words), we do want to ensure that changes to these values are carefully + * reasoned about and properly documented. This is especially the case as + * network-protocol and device-driver modules encode these layouts, and must + * be recompiled if the structures change. Check these values at compile time + * against the ones documented in comments in mbuf.h. + * + * NB: Possibly they should be documented there via #define's and not just + * comments. + */ +#if defined(__LP64__) +CTASSERT(offsetof(struct mbuf, m_dat) == 32); +CTASSERT(sizeof(struct pkthdr) == 56); +CTASSERT(sizeof(struct struct_m_ext) == 48); +#else +CTASSERT(offsetof(struct mbuf, m_dat) == 24); +CTASSERT(sizeof(struct pkthdr) == 48); +CTASSERT(sizeof(struct struct_m_ext) == 28); +#endif /* * m_get2() allocates minimum mbuf that would fit "size" argument. */ struct mbuf * m_get2(int size, int how, short type, int flags) { struct mb_args args; struct mbuf *m, *n; args.flags = flags; args.type = type; if (size <= MHLEN || (size <= MLEN && (flags & M_PKTHDR) == 0)) return (uma_zalloc_arg(zone_mbuf, &args, how)); if (size <= MCLBYTES) return (uma_zalloc_arg(zone_pack, &args, how)); if (size > MJUMPAGESIZE) return (NULL); m = uma_zalloc_arg(zone_mbuf, &args, how); if (m == NULL) return (NULL); n = uma_zalloc_arg(zone_jumbop, m, how); if (n == NULL) { uma_zfree(zone_mbuf, m); return (NULL); } return (m); } /* * m_getjcl() returns an mbuf with a cluster of the specified size attached. * For size it takes MCLBYTES, MJUMPAGESIZE, MJUM9BYTES, MJUM16BYTES. */ struct mbuf * m_getjcl(int how, short type, int flags, int size) { struct mb_args args; struct mbuf *m, *n; uma_zone_t zone; if (size == MCLBYTES) return m_getcl(how, type, flags); args.flags = flags; args.type = type; m = uma_zalloc_arg(zone_mbuf, &args, how); if (m == NULL) return (NULL); zone = m_getzone(size); n = uma_zalloc_arg(zone, m, how); if (n == NULL) { uma_zfree(zone_mbuf, m); return (NULL); } return (m); } /* * Allocate a given length worth of mbufs and/or clusters (whatever fits * best) and return a pointer to the top of the allocated chain. If an * existing mbuf chain is provided, then we will append the new chain * to the existing one but still return the top of the newly allocated * chain. */ struct mbuf * m_getm2(struct mbuf *m, int len, int how, short type, int flags) { struct mbuf *mb, *nm = NULL, *mtail = NULL; KASSERT(len >= 0, ("%s: len is < 0", __func__)); /* Validate flags. */ flags &= (M_PKTHDR | M_EOR); /* Packet header mbuf must be first in chain. */ if ((flags & M_PKTHDR) && m != NULL) flags &= ~M_PKTHDR; /* Loop and append maximum sized mbufs to the chain tail. */ while (len > 0) { if (len > MCLBYTES) mb = m_getjcl(how, type, (flags & M_PKTHDR), MJUMPAGESIZE); else if (len >= MINCLSIZE) mb = m_getcl(how, type, (flags & M_PKTHDR)); else if (flags & M_PKTHDR) mb = m_gethdr(how, type); else mb = m_get(how, type); /* Fail the whole operation if one mbuf can't be allocated. */ if (mb == NULL) { if (nm != NULL) m_freem(nm); return (NULL); } /* Book keeping. */ len -= M_SIZE(mb); if (mtail != NULL) mtail->m_next = mb; else nm = mb; mtail = mb; flags &= ~M_PKTHDR; /* Only valid on the first mbuf. */ } if (flags & M_EOR) mtail->m_flags |= M_EOR; /* Only valid on the last mbuf. */ /* If mbuf was supplied, append new chain to the end of it. */ if (m != NULL) { for (mtail = m; mtail->m_next != NULL; mtail = mtail->m_next) ; mtail->m_next = nm; mtail->m_flags &= ~M_EOR; } else m = nm; return (m); } /* * Free an entire chain of mbufs and associated external buffers, if * applicable. */ void m_freem(struct mbuf *mb) { while (mb != NULL) mb = m_free(mb); } /*- * Configure a provided mbuf to refer to the provided external storage * buffer and setup a reference count for said buffer. If the setting * up of the reference count fails, the M_EXT bit will not be set. If * successfull, the M_EXT bit is set in the mbuf's flags. * * Arguments: * mb The existing mbuf to which to attach the provided buffer. * buf The address of the provided external storage buffer. * size The size of the provided buffer. * freef A pointer to a routine that is responsible for freeing the * provided external storage buffer. * args A pointer to an argument structure (of any type) to be passed * to the provided freef routine (may be NULL). * flags Any other flags to be passed to the provided mbuf. * type The type that the external storage buffer should be * labeled with. * * Returns: * Nothing. */ int m_extadd(struct mbuf *mb, caddr_t buf, u_int size, void (*freef)(struct mbuf *, void *, void *), void *arg1, void *arg2, int flags, int type, int wait) { KASSERT(type != EXT_CLUSTER, ("%s: EXT_CLUSTER not allowed", __func__)); if (type != EXT_EXTREF) mb->m_ext.ext_cnt = uma_zalloc(zone_ext_refcnt, wait); if (mb->m_ext.ext_cnt == NULL) return (ENOMEM); *(mb->m_ext.ext_cnt) = 1; mb->m_flags |= (M_EXT | flags); mb->m_ext.ext_buf = buf; mb->m_data = mb->m_ext.ext_buf; mb->m_ext.ext_size = size; mb->m_ext.ext_free = freef; mb->m_ext.ext_arg1 = arg1; mb->m_ext.ext_arg2 = arg2; mb->m_ext.ext_type = type; mb->m_ext.ext_flags = 0; return (0); } /* * Non-directly-exported function to clean up after mbufs with M_EXT * storage attached to them if the reference count hits 1. */ void mb_free_ext(struct mbuf *m) { int freembuf; KASSERT(m->m_flags & M_EXT, ("%s: M_EXT not set on %p", __func__, m)); /* * Check if the header is embedded in the cluster. */ freembuf = (m->m_flags & M_NOFREE) ? 0 : 1; switch (m->m_ext.ext_type) { case EXT_SFBUF: sf_ext_free(m->m_ext.ext_arg1, m->m_ext.ext_arg2); break; default: KASSERT(m->m_ext.ext_cnt != NULL, ("%s: no refcounting pointer on %p", __func__, m)); /* * Free attached storage if this mbuf is the only * reference to it. */ if (*(m->m_ext.ext_cnt) != 1) { if (atomic_fetchadd_int(m->m_ext.ext_cnt, -1) != 1) break; } switch (m->m_ext.ext_type) { case EXT_PACKET: /* The packet zone is special. */ if (*(m->m_ext.ext_cnt) == 0) *(m->m_ext.ext_cnt) = 1; uma_zfree(zone_pack, m); return; /* Job done. */ case EXT_CLUSTER: uma_zfree(zone_clust, m->m_ext.ext_buf); break; case EXT_JUMBOP: uma_zfree(zone_jumbop, m->m_ext.ext_buf); break; case EXT_JUMBO9: uma_zfree(zone_jumbo9, m->m_ext.ext_buf); break; case EXT_JUMBO16: uma_zfree(zone_jumbo16, m->m_ext.ext_buf); break; case EXT_NET_DRV: case EXT_MOD_TYPE: case EXT_DISPOSABLE: *(m->m_ext.ext_cnt) = 0; uma_zfree(zone_ext_refcnt, __DEVOLATILE(u_int *, m->m_ext.ext_cnt)); /* FALLTHROUGH */ case EXT_EXTREF: KASSERT(m->m_ext.ext_free != NULL, ("%s: ext_free not set", __func__)); (*(m->m_ext.ext_free))(m, m->m_ext.ext_arg1, m->m_ext.ext_arg2); break; default: KASSERT(m->m_ext.ext_type == 0, ("%s: unknown ext_type", __func__)); } } if (freembuf) uma_zfree(zone_mbuf, m); } /* * Attach the cluster from *m to *n, set up m_ext in *n * and bump the refcount of the cluster. */ static void mb_dupcl(struct mbuf *n, struct mbuf *m) { KASSERT(m->m_flags & M_EXT, ("%s: M_EXT not set on %p", __func__, m)); KASSERT(!(n->m_flags & M_EXT), ("%s: M_EXT set on %p", __func__, n)); switch (m->m_ext.ext_type) { case EXT_SFBUF: sf_ext_ref(m->m_ext.ext_arg1, m->m_ext.ext_arg2); break; default: KASSERT(m->m_ext.ext_cnt != NULL, ("%s: no refcounting pointer on %p", __func__, m)); if (*(m->m_ext.ext_cnt) == 1) *(m->m_ext.ext_cnt) += 1; else atomic_add_int(m->m_ext.ext_cnt, 1); } n->m_ext = m->m_ext; n->m_flags |= M_EXT; n->m_flags |= m->m_flags & M_RDONLY; } /* * Clean up mbuf (chain) from any tags and packet headers. * If "all" is set then the first mbuf in the chain will be * cleaned too. */ void m_demote(struct mbuf *m0, int all, int flags) { struct mbuf *m; for (m = all ? m0 : m0->m_next; m != NULL; m = m->m_next) { KASSERT(m->m_nextpkt == NULL, ("%s: m_nextpkt in m %p, m0 %p", __func__, m, m0)); if (m->m_flags & M_PKTHDR) { m_tag_delete_chain(m, NULL); m->m_flags &= ~M_PKTHDR; bzero(&m->m_pkthdr, sizeof(struct pkthdr)); } m->m_flags = m->m_flags & (M_EXT | M_RDONLY | M_NOFREE | flags); } } /* * Sanity checks on mbuf (chain) for use in KASSERT() and general * debugging. * Returns 0 or panics when bad and 1 on all tests passed. * Sanitize, 0 to run M_SANITY_ACTION, 1 to garble things so they * blow up later. */ int m_sanity(struct mbuf *m0, int sanitize) { struct mbuf *m; caddr_t a, b; int pktlen = 0; #ifdef INVARIANTS #define M_SANITY_ACTION(s) panic("mbuf %p: " s, m) #else #define M_SANITY_ACTION(s) printf("mbuf %p: " s, m) #endif for (m = m0; m != NULL; m = m->m_next) { /* * Basic pointer checks. If any of these fails then some * unrelated kernel memory before or after us is trashed. * No way to recover from that. */ a = M_START(m); b = a + M_SIZE(m); if ((caddr_t)m->m_data < a) M_SANITY_ACTION("m_data outside mbuf data range left"); if ((caddr_t)m->m_data > b) M_SANITY_ACTION("m_data outside mbuf data range right"); if ((caddr_t)m->m_data + m->m_len > b) M_SANITY_ACTION("m_data + m_len exeeds mbuf space"); /* m->m_nextpkt may only be set on first mbuf in chain. */ if (m != m0 && m->m_nextpkt != NULL) { if (sanitize) { m_freem(m->m_nextpkt); m->m_nextpkt = (struct mbuf *)0xDEADC0DE; } else M_SANITY_ACTION("m->m_nextpkt on in-chain mbuf"); } /* packet length (not mbuf length!) calculation */ if (m0->m_flags & M_PKTHDR) pktlen += m->m_len; /* m_tags may only be attached to first mbuf in chain. */ if (m != m0 && m->m_flags & M_PKTHDR && !SLIST_EMPTY(&m->m_pkthdr.tags)) { if (sanitize) { m_tag_delete_chain(m, NULL); /* put in 0xDEADC0DE perhaps? */ } else M_SANITY_ACTION("m_tags on in-chain mbuf"); } /* M_PKTHDR may only be set on first mbuf in chain */ if (m != m0 && m->m_flags & M_PKTHDR) { if (sanitize) { bzero(&m->m_pkthdr, sizeof(m->m_pkthdr)); m->m_flags &= ~M_PKTHDR; /* put in 0xDEADCODE and leave hdr flag in */ } else M_SANITY_ACTION("M_PKTHDR on in-chain mbuf"); } } m = m0; if (pktlen && pktlen != m->m_pkthdr.len) { if (sanitize) m->m_pkthdr.len = 0; else M_SANITY_ACTION("m_pkthdr.len != mbuf chain length"); } return 1; #undef M_SANITY_ACTION } /* * "Move" mbuf pkthdr from "from" to "to". * "from" must have M_PKTHDR set, and "to" must be empty. */ void m_move_pkthdr(struct mbuf *to, struct mbuf *from) { #if 0 /* see below for why these are not enabled */ M_ASSERTPKTHDR(to); /* Note: with MAC, this may not be a good assertion. */ KASSERT(SLIST_EMPTY(&to->m_pkthdr.tags), ("m_move_pkthdr: to has tags")); #endif #ifdef MAC /* * XXXMAC: It could be this should also occur for non-MAC? */ if (to->m_flags & M_PKTHDR) m_tag_delete_chain(to, NULL); #endif to->m_flags = (from->m_flags & M_COPYFLAGS) | (to->m_flags & M_EXT); if ((to->m_flags & M_EXT) == 0) to->m_data = to->m_pktdat; to->m_pkthdr = from->m_pkthdr; /* especially tags */ SLIST_INIT(&from->m_pkthdr.tags); /* purge tags from src */ from->m_flags &= ~M_PKTHDR; } /* * Duplicate "from"'s mbuf pkthdr in "to". * "from" must have M_PKTHDR set, and "to" must be empty. * In particular, this does a deep copy of the packet tags. */ int m_dup_pkthdr(struct mbuf *to, struct mbuf *from, int how) { #if 0 /* * The mbuf allocator only initializes the pkthdr * when the mbuf is allocated with m_gethdr(). Many users * (e.g. m_copy*, m_prepend) use m_get() and then * smash the pkthdr as needed causing these * assertions to trip. For now just disable them. */ M_ASSERTPKTHDR(to); /* Note: with MAC, this may not be a good assertion. */ KASSERT(SLIST_EMPTY(&to->m_pkthdr.tags), ("m_dup_pkthdr: to has tags")); #endif MBUF_CHECKSLEEP(how); #ifdef MAC if (to->m_flags & M_PKTHDR) m_tag_delete_chain(to, NULL); #endif to->m_flags = (from->m_flags & M_COPYFLAGS) | (to->m_flags & M_EXT); if ((to->m_flags & M_EXT) == 0) to->m_data = to->m_pktdat; to->m_pkthdr = from->m_pkthdr; SLIST_INIT(&to->m_pkthdr.tags); return (m_tag_copy_chain(to, from, how)); } /* * Lesser-used path for M_PREPEND: * allocate new mbuf to prepend to chain, * copy junk along. */ struct mbuf * m_prepend(struct mbuf *m, int len, int how) { struct mbuf *mn; if (m->m_flags & M_PKTHDR) mn = m_gethdr(how, m->m_type); else mn = m_get(how, m->m_type); if (mn == NULL) { m_freem(m); return (NULL); } if (m->m_flags & M_PKTHDR) m_move_pkthdr(mn, m); mn->m_next = m; m = mn; if (len < M_SIZE(m)) M_ALIGN(m, len); m->m_len = len; return (m); } /* * Make a copy of an mbuf chain starting "off0" bytes from the beginning, * continuing for "len" bytes. If len is M_COPYALL, copy to end of mbuf. * The wait parameter is a choice of M_WAITOK/M_NOWAIT from caller. * Note that the copy is read-only, because clusters are not copied, * only their reference counts are incremented. */ struct mbuf * m_copym(struct mbuf *m, int off0, int len, int wait) { struct mbuf *n, **np; int off = off0; struct mbuf *top; int copyhdr = 0; KASSERT(off >= 0, ("m_copym, negative off %d", off)); KASSERT(len >= 0, ("m_copym, negative len %d", len)); MBUF_CHECKSLEEP(wait); if (off == 0 && m->m_flags & M_PKTHDR) copyhdr = 1; while (off > 0) { KASSERT(m != NULL, ("m_copym, offset > size of mbuf chain")); if (off < m->m_len) break; off -= m->m_len; m = m->m_next; } np = ⊤ top = 0; while (len > 0) { if (m == NULL) { KASSERT(len == M_COPYALL, ("m_copym, length > size of mbuf chain")); break; } if (copyhdr) n = m_gethdr(wait, m->m_type); else n = m_get(wait, m->m_type); *np = n; if (n == NULL) goto nospace; if (copyhdr) { if (!m_dup_pkthdr(n, m, wait)) goto nospace; if (len == M_COPYALL) n->m_pkthdr.len -= off0; else n->m_pkthdr.len = len; copyhdr = 0; } n->m_len = min(len, m->m_len - off); if (m->m_flags & M_EXT) { n->m_data = m->m_data + off; mb_dupcl(n, m); } else bcopy(mtod(m, caddr_t)+off, mtod(n, caddr_t), (u_int)n->m_len); if (len != M_COPYALL) len -= n->m_len; off = 0; m = m->m_next; np = &n->m_next; } return (top); nospace: m_freem(top); return (NULL); } /* * Copy an entire packet, including header (which must be present). * An optimization of the common case `m_copym(m, 0, M_COPYALL, how)'. * Note that the copy is read-only, because clusters are not copied, * only their reference counts are incremented. * Preserve alignment of the first mbuf so if the creator has left * some room at the beginning (e.g. for inserting protocol headers) * the copies still have the room available. */ struct mbuf * m_copypacket(struct mbuf *m, int how) { struct mbuf *top, *n, *o; MBUF_CHECKSLEEP(how); n = m_get(how, m->m_type); top = n; if (n == NULL) goto nospace; if (!m_dup_pkthdr(n, m, how)) goto nospace; n->m_len = m->m_len; if (m->m_flags & M_EXT) { n->m_data = m->m_data; mb_dupcl(n, m); } else { n->m_data = n->m_pktdat + (m->m_data - m->m_pktdat ); bcopy(mtod(m, char *), mtod(n, char *), n->m_len); } m = m->m_next; while (m) { o = m_get(how, m->m_type); if (o == NULL) goto nospace; n->m_next = o; n = n->m_next; n->m_len = m->m_len; if (m->m_flags & M_EXT) { n->m_data = m->m_data; mb_dupcl(n, m); } else { bcopy(mtod(m, char *), mtod(n, char *), n->m_len); } m = m->m_next; } return top; nospace: m_freem(top); return (NULL); } /* * Copy data from an mbuf chain starting "off" bytes from the beginning, * continuing for "len" bytes, into the indicated buffer. */ void m_copydata(const struct mbuf *m, int off, int len, caddr_t cp) { u_int count; KASSERT(off >= 0, ("m_copydata, negative off %d", off)); KASSERT(len >= 0, ("m_copydata, negative len %d", len)); while (off > 0) { KASSERT(m != NULL, ("m_copydata, offset > size of mbuf chain")); if (off < m->m_len) break; off -= m->m_len; m = m->m_next; } while (len > 0) { KASSERT(m != NULL, ("m_copydata, length > size of mbuf chain")); count = min(m->m_len - off, len); bcopy(mtod(m, caddr_t) + off, cp, count); len -= count; cp += count; off = 0; m = m->m_next; } } /* * Copy a packet header mbuf chain into a completely new chain, including * copying any mbuf clusters. Use this instead of m_copypacket() when * you need a writable copy of an mbuf chain. */ struct mbuf * m_dup(struct mbuf *m, int how) { struct mbuf **p, *top = NULL; int remain, moff, nsize; MBUF_CHECKSLEEP(how); /* Sanity check */ if (m == NULL) return (NULL); M_ASSERTPKTHDR(m); /* While there's more data, get a new mbuf, tack it on, and fill it */ remain = m->m_pkthdr.len; moff = 0; p = ⊤ while (remain > 0 || top == NULL) { /* allow m->m_pkthdr.len == 0 */ struct mbuf *n; /* Get the next new mbuf */ if (remain >= MINCLSIZE) { n = m_getcl(how, m->m_type, 0); nsize = MCLBYTES; } else { n = m_get(how, m->m_type); nsize = MLEN; } if (n == NULL) goto nospace; if (top == NULL) { /* First one, must be PKTHDR */ if (!m_dup_pkthdr(n, m, how)) { m_free(n); goto nospace; } if ((n->m_flags & M_EXT) == 0) nsize = MHLEN; } n->m_len = 0; /* Link it into the new chain */ *p = n; p = &n->m_next; /* Copy data from original mbuf(s) into new mbuf */ while (n->m_len < nsize && m != NULL) { int chunk = min(nsize - n->m_len, m->m_len - moff); bcopy(m->m_data + moff, n->m_data + n->m_len, chunk); moff += chunk; n->m_len += chunk; remain -= chunk; if (moff == m->m_len) { m = m->m_next; moff = 0; } } /* Check correct total mbuf length */ KASSERT((remain > 0 && m != NULL) || (remain == 0 && m == NULL), ("%s: bogus m_pkthdr.len", __func__)); } return (top); nospace: m_freem(top); return (NULL); } /* * Concatenate mbuf chain n to m. * Both chains must be of the same type (e.g. MT_DATA). * Any m_pkthdr is not updated. */ void m_cat(struct mbuf *m, struct mbuf *n) { while (m->m_next) m = m->m_next; while (n) { if (!M_WRITABLE(m) || M_TRAILINGSPACE(m) < n->m_len) { /* just join the two chains */ m->m_next = n; return; } /* splat the data from one into the other */ bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len, (u_int)n->m_len); m->m_len += n->m_len; n = m_free(n); } } /* * Concatenate two pkthdr mbuf chains. */ void m_catpkt(struct mbuf *m, struct mbuf *n) { M_ASSERTPKTHDR(m); M_ASSERTPKTHDR(n); m->m_pkthdr.len += n->m_pkthdr.len; m_demote(n, 1, 0); m_cat(m, n); } void m_adj(struct mbuf *mp, int req_len) { int len = req_len; struct mbuf *m; int count; if ((m = mp) == NULL) return; if (len >= 0) { /* * Trim from head. */ while (m != NULL && len > 0) { if (m->m_len <= len) { len -= m->m_len; m->m_len = 0; m = m->m_next; } else { m->m_len -= len; m->m_data += len; len = 0; } } if (mp->m_flags & M_PKTHDR) mp->m_pkthdr.len -= (req_len - len); } else { /* * Trim from tail. Scan the mbuf chain, * calculating its length and finding the last mbuf. * If the adjustment only affects this mbuf, then just * adjust and return. Otherwise, rescan and truncate * after the remaining size. */ len = -len; count = 0; for (;;) { count += m->m_len; if (m->m_next == (struct mbuf *)0) break; m = m->m_next; } if (m->m_len >= len) { m->m_len -= len; if (mp->m_flags & M_PKTHDR) mp->m_pkthdr.len -= len; return; } count -= len; if (count < 0) count = 0; /* * Correct length for chain is "count". * Find the mbuf with last data, adjust its length, * and toss data from remaining mbufs on chain. */ m = mp; if (m->m_flags & M_PKTHDR) m->m_pkthdr.len = count; for (; m; m = m->m_next) { if (m->m_len >= count) { m->m_len = count; if (m->m_next != NULL) { m_freem(m->m_next); m->m_next = NULL; } break; } count -= m->m_len; } } } /* * Rearange an mbuf chain so that len bytes are contiguous * and in the data area of an mbuf (so that mtod will work * for a structure of size len). Returns the resulting * mbuf chain on success, frees it and returns null on failure. * If there is room, it will add up to max_protohdr-len extra bytes to the * contiguous region in an attempt to avoid being called next time. */ struct mbuf * m_pullup(struct mbuf *n, int len) { struct mbuf *m; int count; int space; /* * If first mbuf has no cluster, and has room for len bytes * without shifting current data, pullup into it, * otherwise allocate a new mbuf to prepend to the chain. */ if ((n->m_flags & M_EXT) == 0 && n->m_data + len < &n->m_dat[MLEN] && n->m_next) { if (n->m_len >= len) return (n); m = n; n = n->m_next; len -= m->m_len; } else { if (len > MHLEN) goto bad; m = m_get(M_NOWAIT, n->m_type); if (m == NULL) goto bad; if (n->m_flags & M_PKTHDR) m_move_pkthdr(m, n); } space = &m->m_dat[MLEN] - (m->m_data + m->m_len); do { count = min(min(max(len, max_protohdr), space), n->m_len); bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len, (u_int)count); len -= count; m->m_len += count; n->m_len -= count; space -= count; if (n->m_len) n->m_data += count; else n = m_free(n); } while (len > 0 && n); if (len > 0) { (void) m_free(m); goto bad; } m->m_next = n; return (m); bad: m_freem(n); return (NULL); } /* * Like m_pullup(), except a new mbuf is always allocated, and we allow * the amount of empty space before the data in the new mbuf to be specified * (in the event that the caller expects to prepend later). */ int MSFail; struct mbuf * m_copyup(struct mbuf *n, int len, int dstoff) { struct mbuf *m; int count, space; if (len > (MHLEN - dstoff)) goto bad; m = m_get(M_NOWAIT, n->m_type); if (m == NULL) goto bad; if (n->m_flags & M_PKTHDR) m_move_pkthdr(m, n); m->m_data += dstoff; space = &m->m_dat[MLEN] - (m->m_data + m->m_len); do { count = min(min(max(len, max_protohdr), space), n->m_len); memcpy(mtod(m, caddr_t) + m->m_len, mtod(n, caddr_t), (unsigned)count); len -= count; m->m_len += count; n->m_len -= count; space -= count; if (n->m_len) n->m_data += count; else n = m_free(n); } while (len > 0 && n); if (len > 0) { (void) m_free(m); goto bad; } m->m_next = n; return (m); bad: m_freem(n); MSFail++; return (NULL); } /* * Partition an mbuf chain in two pieces, returning the tail -- * all but the first len0 bytes. In case of failure, it returns NULL and * attempts to restore the chain to its original state. * * Note that the resulting mbufs might be read-only, because the new * mbuf can end up sharing an mbuf cluster with the original mbuf if * the "breaking point" happens to lie within a cluster mbuf. Use the * M_WRITABLE() macro to check for this case. */ struct mbuf * m_split(struct mbuf *m0, int len0, int wait) { struct mbuf *m, *n; u_int len = len0, remain; MBUF_CHECKSLEEP(wait); for (m = m0; m && len > m->m_len; m = m->m_next) len -= m->m_len; if (m == NULL) return (NULL); remain = m->m_len - len; if (m0->m_flags & M_PKTHDR && remain == 0) { n = m_gethdr(wait, m0->m_type); if (n == NULL) return (NULL); n->m_next = m->m_next; m->m_next = NULL; n->m_pkthdr.rcvif = m0->m_pkthdr.rcvif; n->m_pkthdr.len = m0->m_pkthdr.len - len0; m0->m_pkthdr.len = len0; return (n); } else if (m0->m_flags & M_PKTHDR) { n = m_gethdr(wait, m0->m_type); if (n == NULL) return (NULL); n->m_pkthdr.rcvif = m0->m_pkthdr.rcvif; n->m_pkthdr.len = m0->m_pkthdr.len - len0; m0->m_pkthdr.len = len0; if (m->m_flags & M_EXT) goto extpacket; if (remain > MHLEN) { /* m can't be the lead packet */ M_ALIGN(n, 0); n->m_next = m_split(m, len, wait); if (n->m_next == NULL) { (void) m_free(n); return (NULL); } else { n->m_len = 0; return (n); } } else M_ALIGN(n, remain); } else if (remain == 0) { n = m->m_next; m->m_next = NULL; return (n); } else { n = m_get(wait, m->m_type); if (n == NULL) return (NULL); M_ALIGN(n, remain); } extpacket: if (m->m_flags & M_EXT) { n->m_data = m->m_data + len; mb_dupcl(n, m); } else { bcopy(mtod(m, caddr_t) + len, mtod(n, caddr_t), remain); } n->m_len = remain; m->m_len = len; n->m_next = m->m_next; m->m_next = NULL; return (n); } /* * Routine to copy from device local memory into mbufs. * Note that `off' argument is offset into first mbuf of target chain from * which to begin copying the data to. */ struct mbuf * m_devget(char *buf, int totlen, int off, struct ifnet *ifp, void (*copy)(char *from, caddr_t to, u_int len)) { struct mbuf *m; struct mbuf *top = NULL, **mp = ⊤ int len; if (off < 0 || off > MHLEN) return (NULL); while (totlen > 0) { if (top == NULL) { /* First one, must be PKTHDR */ if (totlen + off >= MINCLSIZE) { m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); len = MCLBYTES; } else { m = m_gethdr(M_NOWAIT, MT_DATA); len = MHLEN; /* Place initial small packet/header at end of mbuf */ if (m && totlen + off + max_linkhdr <= MLEN) { m->m_data += max_linkhdr; len -= max_linkhdr; } } if (m == NULL) return NULL; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = totlen; } else { if (totlen + off >= MINCLSIZE) { m = m_getcl(M_NOWAIT, MT_DATA, 0); len = MCLBYTES; } else { m = m_get(M_NOWAIT, MT_DATA); len = MLEN; } if (m == NULL) { m_freem(top); return NULL; } } if (off) { m->m_data += off; len -= off; off = 0; } m->m_len = len = min(totlen, len); if (copy) copy(buf, mtod(m, caddr_t), (u_int)len); else bcopy(buf, mtod(m, caddr_t), (u_int)len); buf += len; *mp = m; mp = &m->m_next; totlen -= len; } return (top); } /* * Copy data from a buffer back into the indicated mbuf chain, * starting "off" bytes from the beginning, extending the mbuf * chain if necessary. */ void m_copyback(struct mbuf *m0, int off, int len, c_caddr_t cp) { int mlen; struct mbuf *m = m0, *n; int totlen = 0; if (m0 == NULL) return; while (off > (mlen = m->m_len)) { off -= mlen; totlen += mlen; if (m->m_next == NULL) { n = m_get(M_NOWAIT, m->m_type); if (n == NULL) goto out; bzero(mtod(n, caddr_t), MLEN); n->m_len = min(MLEN, len + off); m->m_next = n; } m = m->m_next; } while (len > 0) { if (m->m_next == NULL && (len > m->m_len - off)) { m->m_len += min(len - (m->m_len - off), M_TRAILINGSPACE(m)); } mlen = min (m->m_len - off, len); bcopy(cp, off + mtod(m, caddr_t), (u_int)mlen); cp += mlen; len -= mlen; mlen += off; off = 0; totlen += mlen; if (len == 0) break; if (m->m_next == NULL) { n = m_get(M_NOWAIT, m->m_type); if (n == NULL) break; n->m_len = min(MLEN, len); m->m_next = n; } m = m->m_next; } out: if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen)) m->m_pkthdr.len = totlen; } /* * Append the specified data to the indicated mbuf chain, * Extend the mbuf chain if the new data does not fit in * existing space. * * Return 1 if able to complete the job; otherwise 0. */ int m_append(struct mbuf *m0, int len, c_caddr_t cp) { struct mbuf *m, *n; int remainder, space; for (m = m0; m->m_next != NULL; m = m->m_next) ; remainder = len; space = M_TRAILINGSPACE(m); if (space > 0) { /* * Copy into available space. */ if (space > remainder) space = remainder; bcopy(cp, mtod(m, caddr_t) + m->m_len, space); m->m_len += space; cp += space, remainder -= space; } while (remainder > 0) { /* * Allocate a new mbuf; could check space * and allocate a cluster instead. */ n = m_get(M_NOWAIT, m->m_type); if (n == NULL) break; n->m_len = min(MLEN, remainder); bcopy(cp, mtod(n, caddr_t), n->m_len); cp += n->m_len, remainder -= n->m_len; m->m_next = n; m = n; } if (m0->m_flags & M_PKTHDR) m0->m_pkthdr.len += len - remainder; return (remainder == 0); } /* * Apply function f to the data in an mbuf chain starting "off" bytes from * the beginning, continuing for "len" bytes. */ int m_apply(struct mbuf *m, int off, int len, int (*f)(void *, void *, u_int), void *arg) { u_int count; int rval; KASSERT(off >= 0, ("m_apply, negative off %d", off)); KASSERT(len >= 0, ("m_apply, negative len %d", len)); while (off > 0) { KASSERT(m != NULL, ("m_apply, offset > size of mbuf chain")); if (off < m->m_len) break; off -= m->m_len; m = m->m_next; } while (len > 0) { KASSERT(m != NULL, ("m_apply, offset > size of mbuf chain")); count = min(m->m_len - off, len); rval = (*f)(arg, mtod(m, caddr_t) + off, count); if (rval) return (rval); len -= count; off = 0; m = m->m_next; } return (0); } /* * Return a pointer to mbuf/offset of location in mbuf chain. */ struct mbuf * m_getptr(struct mbuf *m, int loc, int *off) { while (loc >= 0) { /* Normal end of search. */ if (m->m_len > loc) { *off = loc; return (m); } else { loc -= m->m_len; if (m->m_next == NULL) { if (loc == 0) { /* Point at the end of valid data. */ *off = m->m_len; return (m); } return (NULL); } m = m->m_next; } } return (NULL); } void m_print(const struct mbuf *m, int maxlen) { int len; int pdata; const struct mbuf *m2; if (m == NULL) { printf("mbuf: %p\n", m); return; } if (m->m_flags & M_PKTHDR) len = m->m_pkthdr.len; else len = -1; m2 = m; while (m2 != NULL && (len == -1 || len)) { pdata = m2->m_len; if (maxlen != -1 && pdata > maxlen) pdata = maxlen; printf("mbuf: %p len: %d, next: %p, %b%s", m2, m2->m_len, m2->m_next, m2->m_flags, "\20\20freelist\17skipfw" "\11proto5\10proto4\7proto3\6proto2\5proto1\4rdonly" "\3eor\2pkthdr\1ext", pdata ? "" : "\n"); if (pdata) printf(", %*D\n", pdata, (u_char *)m2->m_data, "-"); if (len != -1) len -= m2->m_len; m2 = m2->m_next; } if (len > 0) printf("%d bytes unaccounted for.\n", len); return; } u_int m_fixhdr(struct mbuf *m0) { u_int len; len = m_length(m0, NULL); m0->m_pkthdr.len = len; return (len); } u_int m_length(struct mbuf *m0, struct mbuf **last) { struct mbuf *m; u_int len; len = 0; for (m = m0; m != NULL; m = m->m_next) { len += m->m_len; if (m->m_next == NULL) break; } if (last != NULL) *last = m; return (len); } /* * Defragment a mbuf chain, returning the shortest possible * chain of mbufs and clusters. If allocation fails and * this cannot be completed, NULL will be returned, but * the passed in chain will be unchanged. Upon success, * the original chain will be freed, and the new chain * will be returned. * * If a non-packet header is passed in, the original * mbuf (chain?) will be returned unharmed. */ struct mbuf * m_defrag(struct mbuf *m0, int how) { struct mbuf *m_new = NULL, *m_final = NULL; int progress = 0, length; MBUF_CHECKSLEEP(how); if (!(m0->m_flags & M_PKTHDR)) return (m0); m_fixhdr(m0); /* Needed sanity check */ #ifdef MBUF_STRESS_TEST if (m_defragrandomfailures) { int temp = arc4random() & 0xff; if (temp == 0xba) goto nospace; } #endif if (m0->m_pkthdr.len > MHLEN) m_final = m_getcl(how, MT_DATA, M_PKTHDR); else m_final = m_gethdr(how, MT_DATA); if (m_final == NULL) goto nospace; if (m_dup_pkthdr(m_final, m0, how) == 0) goto nospace; m_new = m_final; while (progress < m0->m_pkthdr.len) { length = m0->m_pkthdr.len - progress; if (length > MCLBYTES) length = MCLBYTES; if (m_new == NULL) { if (length > MLEN) m_new = m_getcl(how, MT_DATA, 0); else m_new = m_get(how, MT_DATA); if (m_new == NULL) goto nospace; } m_copydata(m0, progress, length, mtod(m_new, caddr_t)); progress += length; m_new->m_len = length; if (m_new != m_final) m_cat(m_final, m_new); m_new = NULL; } #ifdef MBUF_STRESS_TEST if (m0->m_next == NULL) m_defraguseless++; #endif m_freem(m0); m0 = m_final; #ifdef MBUF_STRESS_TEST m_defragpackets++; m_defragbytes += m0->m_pkthdr.len; #endif return (m0); nospace: #ifdef MBUF_STRESS_TEST m_defragfailure++; #endif if (m_final) m_freem(m_final); return (NULL); } /* * Defragment an mbuf chain, returning at most maxfrags separate * mbufs+clusters. If this is not possible NULL is returned and * the original mbuf chain is left in it's present (potentially * modified) state. We use two techniques: collapsing consecutive * mbufs and replacing consecutive mbufs by a cluster. * * NB: this should really be named m_defrag but that name is taken */ struct mbuf * m_collapse(struct mbuf *m0, int how, int maxfrags) { struct mbuf *m, *n, *n2, **prev; u_int curfrags; /* * Calculate the current number of frags. */ curfrags = 0; for (m = m0; m != NULL; m = m->m_next) curfrags++; /* * First, try to collapse mbufs. Note that we always collapse * towards the front so we don't need to deal with moving the * pkthdr. This may be suboptimal if the first mbuf has much * less data than the following. */ m = m0; again: for (;;) { n = m->m_next; if (n == NULL) break; if (M_WRITABLE(m) && n->m_len < M_TRAILINGSPACE(m)) { bcopy(mtod(n, void *), mtod(m, char *) + m->m_len, n->m_len); m->m_len += n->m_len; m->m_next = n->m_next; m_free(n); if (--curfrags <= maxfrags) return m0; } else m = n; } KASSERT(maxfrags > 1, ("maxfrags %u, but normal collapse failed", maxfrags)); /* * Collapse consecutive mbufs to a cluster. */ prev = &m0->m_next; /* NB: not the first mbuf */ while ((n = *prev) != NULL) { if ((n2 = n->m_next) != NULL && n->m_len + n2->m_len < MCLBYTES) { m = m_getcl(how, MT_DATA, 0); if (m == NULL) goto bad; bcopy(mtod(n, void *), mtod(m, void *), n->m_len); bcopy(mtod(n2, void *), mtod(m, char *) + n->m_len, n2->m_len); m->m_len = n->m_len + n2->m_len; m->m_next = n2->m_next; *prev = m; m_free(n); m_free(n2); if (--curfrags <= maxfrags) /* +1 cl -2 mbufs */ return m0; /* * Still not there, try the normal collapse * again before we allocate another cluster. */ goto again; } prev = &n->m_next; } /* * No place where we can collapse to a cluster; punt. * This can occur if, for example, you request 2 frags * but the packet requires that both be clusters (we * never reallocate the first mbuf to avoid moving the * packet header). */ bad: return NULL; } #ifdef MBUF_STRESS_TEST /* * Fragment an mbuf chain. There's no reason you'd ever want to do * this in normal usage, but it's great for stress testing various * mbuf consumers. * * If fragmentation is not possible, the original chain will be * returned. * * Possible length values: * 0 no fragmentation will occur * > 0 each fragment will be of the specified length * -1 each fragment will be the same random value in length * -2 each fragment's length will be entirely random * (Random values range from 1 to 256) */ struct mbuf * m_fragment(struct mbuf *m0, int how, int length) { struct mbuf *m_new = NULL, *m_final = NULL; int progress = 0; if (!(m0->m_flags & M_PKTHDR)) return (m0); if ((length == 0) || (length < -2)) return (m0); m_fixhdr(m0); /* Needed sanity check */ m_final = m_getcl(how, MT_DATA, M_PKTHDR); if (m_final == NULL) goto nospace; if (m_dup_pkthdr(m_final, m0, how) == 0) goto nospace; m_new = m_final; if (length == -1) length = 1 + (arc4random() & 255); while (progress < m0->m_pkthdr.len) { int fraglen; if (length > 0) fraglen = length; else fraglen = 1 + (arc4random() & 255); if (fraglen > m0->m_pkthdr.len - progress) fraglen = m0->m_pkthdr.len - progress; if (fraglen > MCLBYTES) fraglen = MCLBYTES; if (m_new == NULL) { m_new = m_getcl(how, MT_DATA, 0); if (m_new == NULL) goto nospace; } m_copydata(m0, progress, fraglen, mtod(m_new, caddr_t)); progress += fraglen; m_new->m_len = fraglen; if (m_new != m_final) m_cat(m_final, m_new); m_new = NULL; } m_freem(m0); m0 = m_final; return (m0); nospace: if (m_final) m_freem(m_final); /* Return the original chain on failure */ return (m0); } #endif /* * Copy the contents of uio into a properly sized mbuf chain. */ struct mbuf * m_uiotombuf(struct uio *uio, int how, int len, int align, int flags) { struct mbuf *m, *mb; int error, length; ssize_t total; int progress = 0; /* * len can be zero or an arbitrary large value bound by * the total data supplied by the uio. */ if (len > 0) total = min(uio->uio_resid, len); else total = uio->uio_resid; /* * The smallest unit returned by m_getm2() is a single mbuf * with pkthdr. We can't align past it. */ if (align >= MHLEN) return (NULL); /* * Give us the full allocation or nothing. * If len is zero return the smallest empty mbuf. */ m = m_getm2(NULL, max(total + align, 1), how, MT_DATA, flags); if (m == NULL) return (NULL); m->m_data += align; /* Fill all mbufs with uio data and update header information. */ for (mb = m; mb != NULL; mb = mb->m_next) { length = min(M_TRAILINGSPACE(mb), total - progress); error = uiomove(mtod(mb, void *), length, uio); if (error) { m_freem(m); return (NULL); } mb->m_len = length; progress += length; if (flags & M_PKTHDR) m->m_pkthdr.len += length; } KASSERT(progress == total, ("%s: progress != total", __func__)); return (m); } /* * Copy an mbuf chain into a uio limited by len if set. */ int m_mbuftouio(struct uio *uio, struct mbuf *m, int len) { int error, length, total; int progress = 0; if (len > 0) total = min(uio->uio_resid, len); else total = uio->uio_resid; /* Fill the uio with data from the mbufs. */ for (; m != NULL; m = m->m_next) { length = min(m->m_len, total - progress); error = uiomove(mtod(m, void *), length, uio); if (error) return (error); progress += length; } return (0); } /* * Create a writable copy of the mbuf chain. While doing this * we compact the chain with a goal of producing a chain with * at most two mbufs. The second mbuf in this chain is likely * to be a cluster. The primary purpose of this work is to create * a writable packet for encryption, compression, etc. The * secondary goal is to linearize the data so the data can be * passed to crypto hardware in the most efficient manner possible. */ struct mbuf * m_unshare(struct mbuf *m0, int how) { struct mbuf *m, *mprev; struct mbuf *n, *mfirst, *mlast; int len, off; mprev = NULL; for (m = m0; m != NULL; m = mprev->m_next) { /* * Regular mbufs are ignored unless there's a cluster * in front of it that we can use to coalesce. We do * the latter mainly so later clusters can be coalesced * also w/o having to handle them specially (i.e. convert * mbuf+cluster -> cluster). This optimization is heavily * influenced by the assumption that we're running over * Ethernet where MCLBYTES is large enough that the max * packet size will permit lots of coalescing into a * single cluster. This in turn permits efficient * crypto operations, especially when using hardware. */ if ((m->m_flags & M_EXT) == 0) { if (mprev && (mprev->m_flags & M_EXT) && m->m_len <= M_TRAILINGSPACE(mprev)) { /* XXX: this ignores mbuf types */ memcpy(mtod(mprev, caddr_t) + mprev->m_len, mtod(m, caddr_t), m->m_len); mprev->m_len += m->m_len; mprev->m_next = m->m_next; /* unlink from chain */ m_free(m); /* reclaim mbuf */ #if 0 newipsecstat.ips_mbcoalesced++; #endif } else { mprev = m; } continue; } /* * Writable mbufs are left alone (for now). */ if (M_WRITABLE(m)) { mprev = m; continue; } /* * Not writable, replace with a copy or coalesce with * the previous mbuf if possible (since we have to copy * it anyway, we try to reduce the number of mbufs and * clusters so that future work is easier). */ KASSERT(m->m_flags & M_EXT, ("m_flags 0x%x", m->m_flags)); /* NB: we only coalesce into a cluster or larger */ if (mprev != NULL && (mprev->m_flags & M_EXT) && m->m_len <= M_TRAILINGSPACE(mprev)) { /* XXX: this ignores mbuf types */ memcpy(mtod(mprev, caddr_t) + mprev->m_len, mtod(m, caddr_t), m->m_len); mprev->m_len += m->m_len; mprev->m_next = m->m_next; /* unlink from chain */ m_free(m); /* reclaim mbuf */ #if 0 newipsecstat.ips_clcoalesced++; #endif continue; } /* * Allocate new space to hold the copy and copy the data. * We deal with jumbo mbufs (i.e. m_len > MCLBYTES) by * splitting them into clusters. We could just malloc a * buffer and make it external but too many device drivers * don't know how to break up the non-contiguous memory when * doing DMA. */ n = m_getcl(how, m->m_type, m->m_flags); if (n == NULL) { m_freem(m0); return (NULL); } len = m->m_len; off = 0; mfirst = n; mlast = NULL; for (;;) { int cc = min(len, MCLBYTES); memcpy(mtod(n, caddr_t), mtod(m, caddr_t) + off, cc); n->m_len = cc; if (mlast != NULL) mlast->m_next = n; mlast = n; #if 0 newipsecstat.ips_clcopied++; #endif len -= cc; if (len <= 0) break; off += cc; n = m_getcl(how, m->m_type, m->m_flags); if (n == NULL) { m_freem(mfirst); m_freem(m0); return (NULL); } } n->m_next = m->m_next; if (mprev == NULL) m0 = mfirst; /* new head of chain */ else mprev->m_next = mfirst; /* replace old mbuf */ m_free(m); /* release old mbuf */ mprev = mfirst; } return (m0); } #ifdef MBUF_PROFILING #define MP_BUCKETS 32 /* don't just change this as things may overflow.*/ struct mbufprofile { uintmax_t wasted[MP_BUCKETS]; uintmax_t used[MP_BUCKETS]; uintmax_t segments[MP_BUCKETS]; } mbprof; #define MP_MAXDIGITS 21 /* strlen("16,000,000,000,000,000,000") == 21 */ #define MP_NUMLINES 6 #define MP_NUMSPERLINE 16 #define MP_EXTRABYTES 64 /* > strlen("used:\nwasted:\nsegments:\n") */ /* work out max space needed and add a bit of spare space too */ #define MP_MAXLINE ((MP_MAXDIGITS+1) * MP_NUMSPERLINE) #define MP_BUFSIZE ((MP_MAXLINE * MP_NUMLINES) + 1 + MP_EXTRABYTES) char mbprofbuf[MP_BUFSIZE]; void m_profile(struct mbuf *m) { int segments = 0; int used = 0; int wasted = 0; while (m) { segments++; used += m->m_len; if (m->m_flags & M_EXT) { wasted += MHLEN - sizeof(m->m_ext) + m->m_ext.ext_size - m->m_len; } else { if (m->m_flags & M_PKTHDR) wasted += MHLEN - m->m_len; else wasted += MLEN - m->m_len; } m = m->m_next; } /* be paranoid.. it helps */ if (segments > MP_BUCKETS - 1) segments = MP_BUCKETS - 1; if (used > 100000) used = 100000; if (wasted > 100000) wasted = 100000; /* store in the appropriate bucket */ /* don't bother locking. if it's slightly off, so what? */ mbprof.segments[segments]++; mbprof.used[fls(used)]++; mbprof.wasted[fls(wasted)]++; } static void mbprof_textify(void) { int offset; char *c; uint64_t *p; p = &mbprof.wasted[0]; c = mbprofbuf; offset = snprintf(c, MP_MAXLINE + 10, "wasted:\n" "%ju %ju %ju %ju %ju %ju %ju %ju " "%ju %ju %ju %ju %ju %ju %ju %ju\n", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); #ifdef BIG_ARRAY p = &mbprof.wasted[16]; c += offset; offset = snprintf(c, MP_MAXLINE, "%ju %ju %ju %ju %ju %ju %ju %ju " "%ju %ju %ju %ju %ju %ju %ju %ju\n", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); #endif p = &mbprof.used[0]; c += offset; offset = snprintf(c, MP_MAXLINE + 10, "used:\n" "%ju %ju %ju %ju %ju %ju %ju %ju " "%ju %ju %ju %ju %ju %ju %ju %ju\n", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); #ifdef BIG_ARRAY p = &mbprof.used[16]; c += offset; offset = snprintf(c, MP_MAXLINE, "%ju %ju %ju %ju %ju %ju %ju %ju " "%ju %ju %ju %ju %ju %ju %ju %ju\n", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); #endif p = &mbprof.segments[0]; c += offset; offset = snprintf(c, MP_MAXLINE + 10, "segments:\n" "%ju %ju %ju %ju %ju %ju %ju %ju " "%ju %ju %ju %ju %ju %ju %ju %ju\n", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); #ifdef BIG_ARRAY p = &mbprof.segments[16]; c += offset; offset = snprintf(c, MP_MAXLINE, "%ju %ju %ju %ju %ju %ju %ju %ju " "%ju %ju %ju %ju %ju %ju %ju %jju", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); #endif } static int mbprof_handler(SYSCTL_HANDLER_ARGS) { int error; mbprof_textify(); error = SYSCTL_OUT(req, mbprofbuf, strlen(mbprofbuf) + 1); return (error); } static int mbprof_clr_handler(SYSCTL_HANDLER_ARGS) { int clear, error; clear = 0; error = sysctl_handle_int(oidp, &clear, 0, req); if (error || !req->newptr) return (error); if (clear) { bzero(&mbprof, sizeof(mbprof)); } return (error); } SYSCTL_PROC(_kern_ipc, OID_AUTO, mbufprofile, CTLTYPE_STRING|CTLFLAG_RD, NULL, 0, mbprof_handler, "A", "mbuf profiling statistics"); SYSCTL_PROC(_kern_ipc, OID_AUTO, mbufprofileclr, CTLTYPE_INT|CTLFLAG_RW, NULL, 0, mbprof_clr_handler, "I", "clear mbuf profiling statistics"); #endif Index: projects/ifnet/sys/sys/mbuf.h =================================================================== --- projects/ifnet/sys/sys/mbuf.h (revision 277209) +++ projects/ifnet/sys/sys/mbuf.h (revision 277210) @@ -1,1297 +1,1334 @@ /*- * Copyright (c) 1982, 1986, 1988, 1993 * The Regents of the University of California. * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * @(#)mbuf.h 8.5 (Berkeley) 2/19/95 * $FreeBSD$ */ #ifndef _SYS_MBUF_H_ #define _SYS_MBUF_H_ /* XXX: These includes suck. Sorry! */ #include #ifdef _KERNEL #include #include #ifdef WITNESS #include #endif #endif /* * Mbufs are of a single size, MSIZE (sys/param.h), which includes overhead. * An mbuf may add a single "mbuf cluster" of size MCLBYTES (also in * sys/param.h), which has no additional overhead and is used instead of the * internal data area; this is done when at least MINCLSIZE of data must be * stored. Additionally, it is possible to allocate a separate buffer * externally and attach it to the mbuf in a way similar to that of mbuf * clusters. * * NB: These calculation do not take actual compiler-induced alignment and * padding inside the complete struct mbuf into account. Appropriate * attention is required when changing members of struct mbuf. * * MLEN is data length in a normal mbuf. * MHLEN is data length in an mbuf with pktheader. * MINCLSIZE is a smallest amount of data that should be put into cluster. + * + * Compile-time assertions in uipc_mbuf.c test these values to ensure that + * they are sensible. */ -#define MLEN ((int)(MSIZE - sizeof(struct m_hdr))) -#define MHLEN ((int)(MLEN - sizeof(struct pkthdr))) +struct mbuf; +#define MHSIZE offsetof(struct mbuf, M_dat.M_databuf) +#define MPKTHSIZE offsetof(struct mbuf, M_dat.MH.MH_dat.MH_databuf) +#define MLEN ((int)(MSIZE - MHSIZE)) +#define MHLEN ((int)(MSIZE - MPKTHSIZE)) #define MINCLSIZE (MHLEN + 1) #ifdef _KERNEL /*- * Macro for type conversion: convert mbuf pointer to data pointer of correct * type: * * mtod(m, t) -- Convert mbuf pointer to data pointer of correct type. * mtodo(m, o) -- Same as above but with offset 'o' into data. */ #define mtod(m, t) ((t)((m)->m_data)) #define mtodo(m, o) ((void *)(((m)->m_data) + (o))) /* * Argument structure passed to UMA routines during mbuf and packet * allocations. */ struct mb_args { int flags; /* Flags for mbuf being allocated */ short type; /* Type of mbuf being allocated */ }; #endif /* _KERNEL */ /* * Header present at the beginning of every mbuf. * Size ILP32: 24 * LP64: 32 */ struct m_hdr { union { struct mbuf *m; SLIST_ENTRY(mbuf) slist; STAILQ_ENTRY(mbuf) stailq; } mh_next; /* next buffer in chain */ union { struct mbuf *m; SLIST_ENTRY(mbuf) slist; STAILQ_ENTRY(mbuf) stailq; } mh_nextpkt; /* next chain in queue/record */ caddr_t mh_data; /* location of data */ int32_t mh_len; /* amount of data in this mbuf */ uint32_t mh_type:8, /* type of data in this mbuf */ mh_flags:24; /* flags; see below */ #if !defined(__LP64__) uint32_t mh_pad; /* pad for 64bit alignment */ #endif }; /* * Packet tag structure (see below for details). */ struct m_tag { SLIST_ENTRY(m_tag) m_tag_link; /* List of packet tags */ u_int16_t m_tag_id; /* Tag ID */ u_int16_t m_tag_len; /* Length of data */ u_int32_t m_tag_cookie; /* ABI/Module ID */ void (*m_tag_free)(struct m_tag *); }; /* * Record/packet header in first mbuf of chain; valid only if M_PKTHDR is set. * Size ILP32: 48 * LP64: 56 + * Compile-time assertions in uipc_mbuf.c test these values to ensure that + * they are correct. */ struct pkthdr { struct ifnet *rcvif; /* rcv interface */ SLIST_HEAD(packet_tags, m_tag) tags; /* list of packet tags */ int32_t len; /* total packet length */ /* Layer crossing persistent information. */ uint32_t flowid; /* packet's 4-tuple system */ uint64_t csum_flags; /* checksum and offload features */ uint16_t fibnum; /* this packet should use this fib */ uint8_t cosqos; /* class/quality of service */ uint8_t rsstype; /* hash type */ uint8_t l2hlen; /* layer 2 header length */ uint8_t l3hlen; /* layer 3 header length */ uint8_t l4hlen; /* layer 4 header length */ uint8_t l5hlen; /* layer 5 header length */ union { uint8_t eight[8]; uint16_t sixteen[4]; uint32_t thirtytwo[2]; uint64_t sixtyfour[1]; uintptr_t unintptr[1]; void *ptr; } PH_per; /* Layer specific non-persistent local storage for reassembly, etc. */ union { uint8_t eight[8]; uint16_t sixteen[4]; uint32_t thirtytwo[2]; uint64_t sixtyfour[1]; uintptr_t unintptr[1]; void *ptr; } PH_loc; }; #define ether_vtag PH_per.sixteen[0] #define PH_vt PH_per #define vt_nrecs sixteen[0] #define tso_segsz PH_per.sixteen[1] #define csum_phsum PH_per.sixteen[2] #define csum_data PH_per.thirtytwo[1] #define pkt_tcphdr PH_loc.ptr /* * Description of external storage mapped into mbuf; valid only if M_EXT is * set. * Size ILP32: 28 * LP64: 48 + * Compile-time assertions in uipc_mbuf.c test these values to ensure that + * they are correct. */ -struct m_ext { +struct struct_m_ext { volatile u_int *ext_cnt; /* pointer to ref count info */ caddr_t ext_buf; /* start of buffer */ uint32_t ext_size; /* size of buffer, for ext_free */ uint32_t ext_type:8, /* type of external storage */ ext_flags:24; /* external storage mbuf flags */ void (*ext_free) /* free routine if not the usual */ (struct mbuf *, void *, void *); void *ext_arg1; /* optional argument pointer */ void *ext_arg2; /* optional argument pointer */ }; /* * The core of the mbuf object along with some shortcut defines for practical * purposes. */ struct mbuf { - struct m_hdr m_hdr; + /* + * Header present at the beginning of every mbuf. + * Size ILP32: 24 + * LP64: 32 + * Compile-time assertions in uipc_mbuf.c test these values to ensure + * that they are correct. + */ union { + struct mbuf *m; + SLIST_ENTRY(mbuf) slist; + STAILQ_ENTRY(mbuf) stailq; + } mu_next; /* next buffer in chain */ + union { + struct mbuf *m; + SLIST_ENTRY(mbuf) slist; + STAILQ_ENTRY(mbuf) stailq; + } mu_nextpkt; /* next chain in queue/record */ + caddr_t m_data; /* location of data */ + int32_t m_len; /* amount of data in this mbuf */ + uint32_t m_type:8, /* type of data in this mbuf */ + m_flags:24; /* flags; see below */ +#if !defined(__LP64__) + uint32_t m_pad; /* pad for 64bit alignment */ +#endif + + /* + * A set of optional headers (packet header, external storage header) + * and internal data storage. Historically, these arrays were sized + * to MHLEN (space left after a packet header) and MLEN (space left + * after only a regular mbuf header); they are now variable size in + * order to support future work on variable-size mbufs. + */ + union { struct { struct pkthdr MH_pkthdr; /* M_PKTHDR set */ union { - struct m_ext MH_ext; /* M_EXT set */ - char MH_databuf[MHLEN]; + struct struct_m_ext MH_ext; /* M_EXT set */ + char MH_databuf[0]; } MH_dat; } MH; - char M_databuf[MLEN]; /* !M_PKTHDR, !M_EXT */ + char M_databuf[0]; /* !M_PKTHDR, !M_EXT */ } M_dat; }; -#define m_next m_hdr.mh_next.m -#define m_slist m_hdr.mh_next.slist -#define m_stailq m_hdr.mh_next.stailq -#define m_len m_hdr.mh_len -#define m_data m_hdr.mh_data -#define m_type m_hdr.mh_type -#define m_flags m_hdr.mh_flags -#define m_nextpkt m_hdr.mh_nextpkt.m -#define m_slistpkt m_hdr.mh_nextpkt.slist -#define m_stailqpkt m_hdr.mh_nextpkt.stailq +#define m_next mu_next.m +#define m_slist mu_next.slist +#define m_stailq mu_next.stailq +#define m_nextpkt mu_nextpkt.m +#define m_slistpkt mu_nextpkt.slist +#define m_stailqpkt mu_nextpkt.stailq #define m_pkthdr M_dat.MH.MH_pkthdr #define m_ext M_dat.MH.MH_dat.MH_ext #define m_pktdat M_dat.MH.MH_dat.MH_databuf #define m_dat M_dat.M_databuf /* * mbuf flags of global significance and layer crossing. * Those of only protocol/layer specific significance are to be mapped * to M_PROTO[1-12] and cleared at layer handoff boundaries. * NB: Limited to the lower 24 bits. */ #define M_EXT 0x00000001 /* has associated external storage */ #define M_PKTHDR 0x00000002 /* start of record */ #define M_EOR 0x00000004 /* end of record */ #define M_RDONLY 0x00000008 /* associated data is marked read-only */ #define M_BCAST 0x00000010 /* send/received as link-level broadcast */ #define M_MCAST 0x00000020 /* send/received as link-level multicast */ #define M_PROMISC 0x00000040 /* packet was not for us */ #define M_VLANTAG 0x00000080 /* ether_vtag is valid */ #define M_UNUSED_8 0x00000100 /* --available-- */ #define M_NOFREE 0x00000200 /* do not free mbuf, embedded in cluster */ #define M_PROTO1 0x00001000 /* protocol-specific */ #define M_PROTO2 0x00002000 /* protocol-specific */ #define M_PROTO3 0x00004000 /* protocol-specific */ #define M_PROTO4 0x00008000 /* protocol-specific */ #define M_PROTO5 0x00010000 /* protocol-specific */ #define M_PROTO6 0x00020000 /* protocol-specific */ #define M_PROTO7 0x00040000 /* protocol-specific */ #define M_PROTO8 0x00080000 /* protocol-specific */ #define M_PROTO9 0x00100000 /* protocol-specific */ #define M_PROTO10 0x00200000 /* protocol-specific */ #define M_PROTO11 0x00400000 /* protocol-specific */ #define M_PROTO12 0x00800000 /* protocol-specific */ /* * Flags to purge when crossing layers. */ #define M_PROTOFLAGS \ (M_PROTO1|M_PROTO2|M_PROTO3|M_PROTO4|M_PROTO5|M_PROTO6|M_PROTO7|M_PROTO8|\ M_PROTO9|M_PROTO10|M_PROTO11|M_PROTO12) /* * Flags preserved when copying m_pkthdr. */ #define M_COPYFLAGS \ (M_PKTHDR|M_EOR|M_RDONLY|M_BCAST|M_MCAST|M_PROMISC|M_VLANTAG| \ M_PROTOFLAGS) /* * Mbuf flag description for use with printf(9) %b identifier. */ #define M_FLAG_BITS \ "\20\1M_EXT\2M_PKTHDR\3M_EOR\4M_RDONLY\5M_BCAST\6M_MCAST" \ "\7M_PROMISC\10M_VLANTAG" #define M_FLAG_PROTOBITS \ "\15M_PROTO1\16M_PROTO2\17M_PROTO3\20M_PROTO4\21M_PROTO5" \ "\22M_PROTO6\23M_PROTO7\24M_PROTO8\25M_PROTO9\26M_PROTO10" \ "\27M_PROTO11\30M_PROTO12" #define M_FLAG_PRINTF (M_FLAG_BITS M_FLAG_PROTOBITS) /* * Network interface cards are able to hash protocol fields (such as IPv4 * addresses and TCP port numbers) classify packets into flows. These flows * can then be used to maintain ordering while delivering packets to the OS * via parallel input queues, as well as to provide a stateless affinity * model. NIC drivers can pass up the hash via m->m_pkthdr.flowid, and set * m_flag fields to indicate how the hash should be interpreted by the * network stack. * * Most NICs support RSS, which provides ordering and explicit affinity, and * use the hash m_flag bits to indicate what header fields were covered by * the hash. M_HASHTYPE_OPAQUE can be set by non-RSS cards or configurations * that provide an opaque flow identifier, allowing for ordering and * distribution without explicit affinity. */ /* Microsoft RSS standard hash types */ #define M_HASHTYPE_NONE 0 #define M_HASHTYPE_RSS_IPV4 1 /* IPv4 2-tuple */ #define M_HASHTYPE_RSS_TCP_IPV4 2 /* TCPv4 4-tuple */ #define M_HASHTYPE_RSS_IPV6 3 /* IPv6 2-tuple */ #define M_HASHTYPE_RSS_TCP_IPV6 4 /* TCPv6 4-tuple */ #define M_HASHTYPE_RSS_IPV6_EX 5 /* IPv6 2-tuple + ext hdrs */ #define M_HASHTYPE_RSS_TCP_IPV6_EX 6 /* TCPv6 4-tiple + ext hdrs */ /* Non-standard RSS hash types */ #define M_HASHTYPE_RSS_UDP_IPV4 7 /* IPv4 UDP 4-tuple */ #define M_HASHTYPE_RSS_UDP_IPV4_EX 8 /* IPv4 UDP 4-tuple + ext hdrs */ #define M_HASHTYPE_RSS_UDP_IPV6 9 /* IPv6 UDP 4-tuple */ #define M_HASHTYPE_RSS_UDP_IPV6_EX 10 /* IPv6 UDP 4-tuple + ext hdrs */ #define M_HASHTYPE_OPAQUE 255 /* ordering, not affinity */ #define M_HASHTYPE_CLEAR(m) ((m)->m_pkthdr.rsstype = 0) #define M_HASHTYPE_GET(m) ((m)->m_pkthdr.rsstype) #define M_HASHTYPE_SET(m, v) ((m)->m_pkthdr.rsstype = (v)) #define M_HASHTYPE_TEST(m, v) (M_HASHTYPE_GET(m) == (v)) /* * COS/QOS class and quality of service tags. * It uses DSCP code points as base. */ #define QOS_DSCP_CS0 0x00 #define QOS_DSCP_DEF QOS_DSCP_CS0 #define QOS_DSCP_CS1 0x20 #define QOS_DSCP_AF11 0x28 #define QOS_DSCP_AF12 0x30 #define QOS_DSCP_AF13 0x38 #define QOS_DSCP_CS2 0x40 #define QOS_DSCP_AF21 0x48 #define QOS_DSCP_AF22 0x50 #define QOS_DSCP_AF23 0x58 #define QOS_DSCP_CS3 0x60 #define QOS_DSCP_AF31 0x68 #define QOS_DSCP_AF32 0x70 #define QOS_DSCP_AF33 0x78 #define QOS_DSCP_CS4 0x80 #define QOS_DSCP_AF41 0x88 #define QOS_DSCP_AF42 0x90 #define QOS_DSCP_AF43 0x98 #define QOS_DSCP_CS5 0xa0 #define QOS_DSCP_EF 0xb8 #define QOS_DSCP_CS6 0xc0 #define QOS_DSCP_CS7 0xe0 /* * External mbuf storage buffer types. */ #define EXT_CLUSTER 1 /* mbuf cluster */ #define EXT_SFBUF 2 /* sendfile(2)'s sf_bufs */ #define EXT_JUMBOP 3 /* jumbo cluster 4096 bytes */ #define EXT_JUMBO9 4 /* jumbo cluster 9216 bytes */ #define EXT_JUMBO16 5 /* jumbo cluster 16184 bytes */ #define EXT_PACKET 6 /* mbuf+cluster from packet zone */ #define EXT_MBUF 7 /* external mbuf reference (M_IOVEC) */ #define EXT_VENDOR1 224 /* for vendor-internal use */ #define EXT_VENDOR2 225 /* for vendor-internal use */ #define EXT_VENDOR3 226 /* for vendor-internal use */ #define EXT_VENDOR4 227 /* for vendor-internal use */ #define EXT_EXP1 244 /* for experimental use */ #define EXT_EXP2 245 /* for experimental use */ #define EXT_EXP3 246 /* for experimental use */ #define EXT_EXP4 247 /* for experimental use */ #define EXT_NET_DRV 252 /* custom ext_buf provided by net driver(s) */ #define EXT_MOD_TYPE 253 /* custom module's ext_buf type */ #define EXT_DISPOSABLE 254 /* can throw this buffer away w/page flipping */ #define EXT_EXTREF 255 /* has externally maintained ext_cnt ptr */ /* * Flags for external mbuf buffer types. * NB: limited to the lower 24 bits. */ #define EXT_FLAG_EMBREF 0x000001 /* embedded ext_cnt, notyet */ #define EXT_FLAG_EXTREF 0x000002 /* external ext_cnt, notyet */ #define EXT_FLAG_NOFREE 0x000010 /* don't free mbuf to pool, notyet */ #define EXT_FLAG_VENDOR1 0x010000 /* for vendor-internal use */ #define EXT_FLAG_VENDOR2 0x020000 /* for vendor-internal use */ #define EXT_FLAG_VENDOR3 0x040000 /* for vendor-internal use */ #define EXT_FLAG_VENDOR4 0x080000 /* for vendor-internal use */ #define EXT_FLAG_EXP1 0x100000 /* for experimental use */ #define EXT_FLAG_EXP2 0x200000 /* for experimental use */ #define EXT_FLAG_EXP3 0x400000 /* for experimental use */ #define EXT_FLAG_EXP4 0x800000 /* for experimental use */ /* * EXT flag description for use with printf(9) %b identifier. */ #define EXT_FLAG_BITS \ "\20\1EXT_FLAG_EMBREF\2EXT_FLAG_EXTREF\5EXT_FLAG_NOFREE" \ "\21EXT_FLAG_VENDOR1\22EXT_FLAG_VENDOR2\23EXT_FLAG_VENDOR3" \ "\24EXT_FLAG_VENDOR4\25EXT_FLAG_EXP1\26EXT_FLAG_EXP2\27EXT_FLAG_EXP3" \ "\30EXT_FLAG_EXP4" /* * External reference/free functions. */ void sf_ext_ref(void *, void *); void sf_ext_free(void *, void *); /* * Flags indicating checksum, segmentation and other offload work to be * done, or already done, by hardware or lower layers. It is split into * separate inbound and outbound flags. * * Outbound flags that are set by upper protocol layers requesting lower * layers, or ideally the hardware, to perform these offloading tasks. * For outbound packets this field and its flags can be directly tested * against ifnet if_hwassist. */ #define CSUM_IP 0x00000001 /* IP header checksum offload */ #define CSUM_IP_UDP 0x00000002 /* UDP checksum offload */ #define CSUM_IP_TCP 0x00000004 /* TCP checksum offload */ #define CSUM_IP_SCTP 0x00000008 /* SCTP checksum offload */ #define CSUM_IP_TSO 0x00000010 /* TCP segmentation offload */ #define CSUM_IP_ISCSI 0x00000020 /* iSCSI checksum offload */ #define CSUM_IP6_UDP 0x00000200 /* UDP checksum offload */ #define CSUM_IP6_TCP 0x00000400 /* TCP checksum offload */ #define CSUM_IP6_SCTP 0x00000800 /* SCTP checksum offload */ #define CSUM_IP6_TSO 0x00001000 /* TCP segmentation offload */ #define CSUM_IP6_ISCSI 0x00002000 /* iSCSI checksum offload */ /* Inbound checksum support where the checksum was verified by hardware. */ #define CSUM_L3_CALC 0x01000000 /* calculated layer 3 csum */ #define CSUM_L3_VALID 0x02000000 /* checksum is correct */ #define CSUM_L4_CALC 0x04000000 /* calculated layer 4 csum */ #define CSUM_L4_VALID 0x08000000 /* checksum is correct */ #define CSUM_L5_CALC 0x10000000 /* calculated layer 5 csum */ #define CSUM_L5_VALID 0x20000000 /* checksum is correct */ #define CSUM_COALESED 0x40000000 /* contains merged segments */ /* * CSUM flag description for use with printf(9) %b identifier. */ #define CSUM_BITS \ "\20\1CSUM_IP\2CSUM_IP_UDP\3CSUM_IP_TCP\4CSUM_IP_SCTP\5CSUM_IP_TSO" \ "\6CSUM_IP_ISCSI" \ "\12CSUM_IP6_UDP\13CSUM_IP6_TCP\14CSUM_IP6_SCTP\15CSUM_IP6_TSO" \ "\16CSUM_IP6_ISCSI" \ "\31CSUM_L3_CALC\32CSUM_L3_VALID\33CSUM_L4_CALC\34CSUM_L4_VALID" \ "\35CSUM_L5_CALC\36CSUM_L5_VALID\37CSUM_COALESED" /* CSUM flags compatibility mappings. */ #define CSUM_IP_CHECKED CSUM_L3_CALC #define CSUM_IP_VALID CSUM_L3_VALID #define CSUM_DATA_VALID CSUM_L4_VALID #define CSUM_PSEUDO_HDR CSUM_L4_CALC #define CSUM_SCTP_VALID CSUM_L4_VALID #define CSUM_DELAY_DATA (CSUM_TCP|CSUM_UDP) #define CSUM_DELAY_IP CSUM_IP /* Only v4, no v6 IP hdr csum */ #define CSUM_DELAY_DATA_IPV6 (CSUM_TCP_IPV6|CSUM_UDP_IPV6) #define CSUM_DATA_VALID_IPV6 CSUM_DATA_VALID #define CSUM_TCP CSUM_IP_TCP #define CSUM_UDP CSUM_IP_UDP #define CSUM_SCTP CSUM_IP_SCTP #define CSUM_TSO (CSUM_IP_TSO|CSUM_IP6_TSO) #define CSUM_UDP_IPV6 CSUM_IP6_UDP #define CSUM_TCP_IPV6 CSUM_IP6_TCP #define CSUM_SCTP_IPV6 CSUM_IP6_SCTP /* * mbuf types describing the content of the mbuf (including external storage). */ #define MT_NOTMBUF 0 /* USED INTERNALLY ONLY! Object is not mbuf */ #define MT_DATA 1 /* dynamic (data) allocation */ #define MT_HEADER MT_DATA /* packet header, use M_PKTHDR instead */ #define MT_VENDOR1 4 /* for vendor-internal use */ #define MT_VENDOR2 5 /* for vendor-internal use */ #define MT_VENDOR3 6 /* for vendor-internal use */ #define MT_VENDOR4 7 /* for vendor-internal use */ #define MT_SONAME 8 /* socket name */ #define MT_EXP1 9 /* for experimental use */ #define MT_EXP2 10 /* for experimental use */ #define MT_EXP3 11 /* for experimental use */ #define MT_EXP4 12 /* for experimental use */ #define MT_CONTROL 14 /* extra-data protocol message */ #define MT_OOBDATA 15 /* expedited data */ #define MT_NTYPES 16 /* number of mbuf types for mbtypes[] */ #define MT_NOINIT 255 /* Not a type but a flag to allocate a non-initialized mbuf */ /* * String names of mbuf-related UMA(9) and malloc(9) types. Exposed to * !_KERNEL so that monitoring tools can look up the zones with * libmemstat(3). */ #define MBUF_MEM_NAME "mbuf" #define MBUF_CLUSTER_MEM_NAME "mbuf_cluster" #define MBUF_PACKET_MEM_NAME "mbuf_packet" #define MBUF_JUMBOP_MEM_NAME "mbuf_jumbo_page" #define MBUF_JUMBO9_MEM_NAME "mbuf_jumbo_9k" #define MBUF_JUMBO16_MEM_NAME "mbuf_jumbo_16k" #define MBUF_TAG_MEM_NAME "mbuf_tag" #define MBUF_EXTREFCNT_MEM_NAME "mbuf_ext_refcnt" #ifdef _KERNEL #ifdef WITNESS #define MBUF_CHECKSLEEP(how) do { \ if (how == M_WAITOK) \ WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, \ "Sleeping in \"%s\"", __func__); \ } while (0) #else #define MBUF_CHECKSLEEP(how) #endif /* * Network buffer allocation API * * The rest of it is defined in kern/kern_mbuf.c */ extern uma_zone_t zone_mbuf; extern uma_zone_t zone_clust; extern uma_zone_t zone_pack; extern uma_zone_t zone_jumbop; extern uma_zone_t zone_jumbo9; extern uma_zone_t zone_jumbo16; extern uma_zone_t zone_ext_refcnt; void mb_free_ext(struct mbuf *); int m_pkthdr_init(struct mbuf *, int); static __inline int m_gettype(int size) { int type; switch (size) { case MSIZE: type = EXT_MBUF; break; case MCLBYTES: type = EXT_CLUSTER; break; #if MJUMPAGESIZE != MCLBYTES case MJUMPAGESIZE: type = EXT_JUMBOP; break; #endif case MJUM9BYTES: type = EXT_JUMBO9; break; case MJUM16BYTES: type = EXT_JUMBO16; break; default: panic("%s: invalid cluster size %d", __func__, size); } return (type); } /* * Associated an external reference counted buffer with an mbuf. */ static __inline void m_extaddref(struct mbuf *m, caddr_t buf, u_int size, u_int *ref_cnt, void (*freef)(struct mbuf *, void *, void *), void *arg1, void *arg2) { KASSERT(ref_cnt != NULL, ("%s: ref_cnt not provided", __func__)); atomic_add_int(ref_cnt, 1); m->m_flags |= M_EXT; m->m_ext.ext_buf = buf; m->m_ext.ext_cnt = ref_cnt; m->m_data = m->m_ext.ext_buf; m->m_ext.ext_size = size; m->m_ext.ext_free = freef; m->m_ext.ext_arg1 = arg1; m->m_ext.ext_arg2 = arg2; m->m_ext.ext_type = EXT_EXTREF; } static __inline uma_zone_t m_getzone(int size) { uma_zone_t zone; switch (size) { case MCLBYTES: zone = zone_clust; break; #if MJUMPAGESIZE != MCLBYTES case MJUMPAGESIZE: zone = zone_jumbop; break; #endif case MJUM9BYTES: zone = zone_jumbo9; break; case MJUM16BYTES: zone = zone_jumbo16; break; default: panic("%s: invalid cluster size %d", __func__, size); } return (zone); } /* * Initialize an mbuf with linear storage. * * Inline because the consumer text overhead will be roughly the same to * initialize or call a function with this many parameters and M_PKTHDR * should go away with constant propagation for !MGETHDR. */ static __inline int m_init(struct mbuf *m, uma_zone_t zone, int size, int how, short type, int flags) { int error; m->m_next = NULL; m->m_nextpkt = NULL; m->m_data = m->m_dat; m->m_len = 0; m->m_flags = flags; m->m_type = type; if (flags & M_PKTHDR) { if ((error = m_pkthdr_init(m, how)) != 0) return (error); } return (0); } static __inline struct mbuf * m_get(int how, short type) { struct mb_args args; args.flags = 0; args.type = type; return (uma_zalloc_arg(zone_mbuf, &args, how)); } /* * XXX This should be deprecated, very little use. */ static __inline struct mbuf * m_getclr(int how, short type) { struct mbuf *m; struct mb_args args; args.flags = 0; args.type = type; m = uma_zalloc_arg(zone_mbuf, &args, how); if (m != NULL) bzero(m->m_data, MLEN); return (m); } static __inline struct mbuf * m_gethdr(int how, short type) { struct mb_args args; args.flags = M_PKTHDR; args.type = type; return (uma_zalloc_arg(zone_mbuf, &args, how)); } static __inline struct mbuf * m_getcl(int how, short type, int flags) { struct mb_args args; args.flags = flags; args.type = type; return (uma_zalloc_arg(zone_pack, &args, how)); } static __inline int m_clget(struct mbuf *m, int how) { if (m->m_flags & M_EXT) printf("%s: %p mbuf already has external storage\n", __func__, m); m->m_ext.ext_buf = (char *)NULL; uma_zalloc_arg(zone_clust, m, how); /* * On a cluster allocation failure, drain the packet zone and retry, * we might be able to loosen a few clusters up on the drain. */ if ((how & M_NOWAIT) && (m->m_ext.ext_buf == NULL)) { zone_drain(zone_pack); uma_zalloc_arg(zone_clust, m, how); } return (m->m_flags & M_EXT); } /* * m_cljget() is different from m_clget() as it can allocate clusters without * attaching them to an mbuf. In that case the return value is the pointer * to the cluster of the requested size. If an mbuf was specified, it gets * the cluster attached to it and the return value can be safely ignored. * For size it takes MCLBYTES, MJUMPAGESIZE, MJUM9BYTES, MJUM16BYTES. */ static __inline void * m_cljget(struct mbuf *m, int how, int size) { uma_zone_t zone; if (m && m->m_flags & M_EXT) printf("%s: %p mbuf already has external storage\n", __func__, m); if (m != NULL) m->m_ext.ext_buf = NULL; zone = m_getzone(size); return (uma_zalloc_arg(zone, m, how)); } static __inline void m_cljset(struct mbuf *m, void *cl, int type) { uma_zone_t zone; int size; switch (type) { case EXT_CLUSTER: size = MCLBYTES; zone = zone_clust; break; #if MJUMPAGESIZE != MCLBYTES case EXT_JUMBOP: size = MJUMPAGESIZE; zone = zone_jumbop; break; #endif case EXT_JUMBO9: size = MJUM9BYTES; zone = zone_jumbo9; break; case EXT_JUMBO16: size = MJUM16BYTES; zone = zone_jumbo16; break; default: panic("%s: unknown cluster type %d", __func__, type); break; } m->m_data = m->m_ext.ext_buf = cl; m->m_ext.ext_free = m->m_ext.ext_arg1 = m->m_ext.ext_arg2 = NULL; m->m_ext.ext_size = size; m->m_ext.ext_type = type; m->m_ext.ext_flags = 0; m->m_ext.ext_cnt = uma_find_refcnt(zone, cl); m->m_flags |= M_EXT; } static __inline void m_chtype(struct mbuf *m, short new_type) { m->m_type = new_type; } static __inline void m_clrprotoflags(struct mbuf *m) { while (m) { m->m_flags &= ~M_PROTOFLAGS; m = m->m_next; } } static __inline struct mbuf * m_last(struct mbuf *m) { while (m->m_next) m = m->m_next; return (m); } /* * mbuf, cluster, and external object allocation macros (for compatibility * purposes). */ #define M_MOVE_PKTHDR(to, from) m_move_pkthdr((to), (from)) #define MGET(m, how, type) ((m) = m_get((how), (type))) #define MGETHDR(m, how, type) ((m) = m_gethdr((how), (type))) #define MCLGET(m, how) m_clget((m), (how)) #define MEXTADD(m, buf, size, free, arg1, arg2, flags, type) \ (void )m_extadd((m), (caddr_t)(buf), (size), (free), (arg1), (arg2),\ (flags), (type), M_NOWAIT) #define m_getm(m, len, how, type) \ m_getm2((m), (len), (how), (type), M_PKTHDR) /* * Evaluate TRUE if it's safe to write to the mbuf m's data region (this can * be both the local data payload, or an external buffer area, depending on * whether M_EXT is set). */ #define M_WRITABLE(m) (!((m)->m_flags & M_RDONLY) && \ (!(((m)->m_flags & M_EXT)) || \ (*((m)->m_ext.ext_cnt) == 1)) ) \ /* Check if the supplied mbuf has a packet header, or else panic. */ #define M_ASSERTPKTHDR(m) \ KASSERT((m) != NULL && (m)->m_flags & M_PKTHDR, \ ("%s: no mbuf packet header!", __func__)) /* * Ensure that the supplied mbuf is a valid, non-free mbuf. * * XXX: Broken at the moment. Need some UMA magic to make it work again. */ #define M_ASSERTVALID(m) \ KASSERT((((struct mbuf *)m)->m_flags & 0) == 0, \ ("%s: attempted use of a free mbuf!", __func__)) /* * Return the address of the start of the buffer associated with an mbuf, * handling external storage, packet-header mbufs, and regular data mbufs. */ #define M_START(m) \ (((m)->m_flags & M_EXT) ? (m)->m_ext.ext_buf : \ ((m)->m_flags & M_PKTHDR) ? &(m)->m_pktdat[0] : \ &(m)->m_dat[0]) /* * Return the size of the buffer associated with an mbuf, handling external * storage, packet-header mbufs, and regular data mbufs. */ #define M_SIZE(m) \ (((m)->m_flags & M_EXT) ? (m)->m_ext.ext_size : \ ((m)->m_flags & M_PKTHDR) ? MHLEN : \ MLEN) /* * Set the m_data pointer of a newly allocated mbuf to place an object of the * specified size at the end of the mbuf, longword aligned. * * NB: Historically, we had M_ALIGN(), MH_ALIGN(), and MEXT_ALIGN() as * separate macros, each asserting that it was called at the proper moment. * This required callers to themselves test the storage type and call the * right one. Rather than require callers to be aware of those layout * decisions, we centralize here. */ static __inline void m_align(struct mbuf *m, int len) { #ifdef INVARIANTS const char *msg = "%s: not a virgin mbuf"; #endif int adjust; KASSERT(m->m_data == M_START(m), (msg, __func__)); adjust = M_SIZE(m) - len; m->m_data += adjust &~ (sizeof(long)-1); } #define M_ALIGN(m, len) m_align(m, len) #define MH_ALIGN(m, len) m_align(m, len) #define MEXT_ALIGN(m, len) m_align(m, len) /* * Compute the amount of space available before the current start of data in * an mbuf. * * The M_WRITABLE() is a temporary, conservative safety measure: the burden * of checking writability of the mbuf data area rests solely with the caller. * * NB: In previous versions, M_LEADINGSPACE() would only check M_WRITABLE() * for mbufs with external storage. We now allow mbuf-embedded data to be * read-only as well. */ #define M_LEADINGSPACE(m) \ (M_WRITABLE(m) ? ((m)->m_data - M_START(m)) : 0) /* * Compute the amount of space available after the end of data in an mbuf. * * The M_WRITABLE() is a temporary, conservative safety measure: the burden * of checking writability of the mbuf data area rests solely with the caller. * * NB: In previous versions, M_TRAILINGSPACE() would only check M_WRITABLE() * for mbufs with external storage. We now allow mbuf-embedded data to be * read-only as well. */ #define M_TRAILINGSPACE(m) \ (M_WRITABLE(m) ? \ ((M_START(m) + M_SIZE(m)) - ((m)->m_data + (m)->m_len)) : 0) /* * Arrange to prepend space of size plen to mbuf m. If a new mbuf must be * allocated, how specifies whether to wait. If the allocation fails, the * original mbuf chain is freed and m is set to NULL. */ #define M_PREPEND(m, plen, how) do { \ struct mbuf **_mmp = &(m); \ struct mbuf *_mm = *_mmp; \ int _mplen = (plen); \ int __mhow = (how); \ \ MBUF_CHECKSLEEP(how); \ if (M_LEADINGSPACE(_mm) >= _mplen) { \ _mm->m_data -= _mplen; \ _mm->m_len += _mplen; \ } else \ _mm = m_prepend(_mm, _mplen, __mhow); \ if (_mm != NULL && _mm->m_flags & M_PKTHDR) \ _mm->m_pkthdr.len += _mplen; \ *_mmp = _mm; \ } while (0) /* * Change mbuf to new type. This is a relatively expensive operation and * should be avoided. */ #define MCHTYPE(m, t) m_chtype((m), (t)) /* Length to m_copy to copy all. */ #define M_COPYALL 1000000000 /* Compatibility with 4.3. */ #define m_copy(m, o, l) m_copym((m), (o), (l), M_NOWAIT) extern int max_datalen; /* MHLEN - max_hdr */ extern int max_hdr; /* Largest link + protocol header */ extern int max_linkhdr; /* Largest link-level header */ extern int max_protohdr; /* Largest protocol header */ extern int nmbclusters; /* Maximum number of clusters */ struct uio; void m_adj(struct mbuf *, int); int m_apply(struct mbuf *, int, int, int (*)(void *, void *, u_int), void *); int m_append(struct mbuf *, int, c_caddr_t); void m_cat(struct mbuf *, struct mbuf *); void m_catpkt(struct mbuf *, struct mbuf *); int m_extadd(struct mbuf *, caddr_t, u_int, void (*)(struct mbuf *, void *, void *), void *, void *, int, int, int); struct mbuf *m_collapse(struct mbuf *, int, int); void m_copyback(struct mbuf *, int, int, c_caddr_t); void m_copydata(const struct mbuf *, int, int, caddr_t); struct mbuf *m_copym(struct mbuf *, int, int, int); struct mbuf *m_copypacket(struct mbuf *, int); void m_copy_pkthdr(struct mbuf *, struct mbuf *); struct mbuf *m_copyup(struct mbuf *, int, int); struct mbuf *m_defrag(struct mbuf *, int); void m_demote(struct mbuf *, int, int); struct mbuf *m_devget(char *, int, int, struct ifnet *, void (*)(char *, caddr_t, u_int)); struct mbuf *m_dup(struct mbuf *, int); int m_dup_pkthdr(struct mbuf *, struct mbuf *, int); u_int m_fixhdr(struct mbuf *); struct mbuf *m_fragment(struct mbuf *, int, int); void m_freem(struct mbuf *); struct mbuf *m_get2(int, int, short, int); struct mbuf *m_getjcl(int, short, int, int); struct mbuf *m_getm2(struct mbuf *, int, int, short, int); struct mbuf *m_getptr(struct mbuf *, int, int *); u_int m_length(struct mbuf *, struct mbuf **); int m_mbuftouio(struct uio *, struct mbuf *, int); void m_move_pkthdr(struct mbuf *, struct mbuf *); struct mbuf *m_prepend(struct mbuf *, int, int); void m_print(const struct mbuf *, int); struct mbuf *m_pulldown(struct mbuf *, int, int, int *); struct mbuf *m_pullup(struct mbuf *, int); int m_sanity(struct mbuf *, int); struct mbuf *m_split(struct mbuf *, int, int); struct mbuf *m_uiotombuf(struct uio *, int, int, int, int); struct mbuf *m_unshare(struct mbuf *, int); /*- * Network packets may have annotations attached by affixing a list of * "packet tags" to the pkthdr structure. Packet tags are dynamically * allocated semi-opaque data structures that have a fixed header * (struct m_tag) that specifies the size of the memory block and a * pair that identifies it. The cookie is a 32-bit unique * unsigned value used to identify a module or ABI. By convention this value * is chosen as the date+time that the module is created, expressed as the * number of seconds since the epoch (e.g., using date -u +'%s'). The type * value is an ABI/module-specific value that identifies a particular * annotation and is private to the module. For compatibility with systems * like OpenBSD that define packet tags w/o an ABI/module cookie, the value * PACKET_ABI_COMPAT is used to implement m_tag_get and m_tag_find * compatibility shim functions and several tag types are defined below. * Users that do not require compatibility should use a private cookie value * so that packet tag-related definitions can be maintained privately. * * Note that the packet tag returned by m_tag_alloc has the default memory * alignment implemented by malloc. To reference private data one can use a * construct like: * * struct m_tag *mtag = m_tag_alloc(...); * struct foo *p = (struct foo *)(mtag+1); * * if the alignment of struct m_tag is sufficient for referencing members of * struct foo. Otherwise it is necessary to embed struct m_tag within the * private data structure to insure proper alignment; e.g., * * struct foo { * struct m_tag tag; * ... * }; * struct foo *p = (struct foo *) m_tag_alloc(...); * struct m_tag *mtag = &p->tag; */ /* * Persistent tags stay with an mbuf until the mbuf is reclaimed. Otherwise * tags are expected to ``vanish'' when they pass through a network * interface. For most interfaces this happens normally as the tags are * reclaimed when the mbuf is free'd. However in some special cases * reclaiming must be done manually. An example is packets that pass through * the loopback interface. Also, one must be careful to do this when * ``turning around'' packets (e.g., icmp_reflect). * * To mark a tag persistent bit-or this flag in when defining the tag id. * The tag will then be treated as described above. */ #define MTAG_PERSISTENT 0x800 #define PACKET_TAG_NONE 0 /* Nadda */ /* Packet tags for use with PACKET_ABI_COMPAT. */ #define PACKET_TAG_IPSEC_IN_DONE 1 /* IPsec applied, in */ #define PACKET_TAG_IPSEC_OUT_DONE 2 /* IPsec applied, out */ #define PACKET_TAG_IPSEC_IN_CRYPTO_DONE 3 /* NIC IPsec crypto done */ #define PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED 4 /* NIC IPsec crypto req'ed */ #define PACKET_TAG_IPSEC_IN_COULD_DO_CRYPTO 5 /* NIC notifies IPsec */ #define PACKET_TAG_IPSEC_PENDING_TDB 6 /* Reminder to do IPsec */ #define PACKET_TAG_BRIDGE 7 /* Bridge processing done */ #define PACKET_TAG_GIF 8 /* GIF processing done */ #define PACKET_TAG_GRE 9 /* GRE processing done */ #define PACKET_TAG_IN_PACKET_CHECKSUM 10 /* NIC checksumming done */ #define PACKET_TAG_ENCAP 11 /* Encap. processing */ #define PACKET_TAG_IPSEC_SOCKET 12 /* IPSEC socket ref */ #define PACKET_TAG_IPSEC_HISTORY 13 /* IPSEC history */ #define PACKET_TAG_IPV6_INPUT 14 /* IPV6 input processing */ #define PACKET_TAG_DUMMYNET 15 /* dummynet info */ #define PACKET_TAG_DIVERT 17 /* divert info */ #define PACKET_TAG_IPFORWARD 18 /* ipforward info */ #define PACKET_TAG_MACLABEL (19 | MTAG_PERSISTENT) /* MAC label */ #define PACKET_TAG_PF (21 | MTAG_PERSISTENT) /* PF/ALTQ information */ #define PACKET_TAG_RTSOCKFAM 25 /* rtsock sa family */ #define PACKET_TAG_IPOPTIONS 27 /* Saved IP options */ #define PACKET_TAG_CARP 28 /* CARP info */ #define PACKET_TAG_IPSEC_NAT_T_PORTS 29 /* two uint16_t */ #define PACKET_TAG_ND_OUTGOING 30 /* ND outgoing */ /* Specific cookies and tags. */ /* Packet tag routines. */ struct m_tag *m_tag_alloc(u_int32_t, int, int, int); void m_tag_delete(struct mbuf *, struct m_tag *); void m_tag_delete_chain(struct mbuf *, struct m_tag *); void m_tag_free_default(struct m_tag *); struct m_tag *m_tag_locate(struct mbuf *, u_int32_t, int, struct m_tag *); struct m_tag *m_tag_copy(struct m_tag *, int); int m_tag_copy_chain(struct mbuf *, struct mbuf *, int); void m_tag_delete_nonpersistent(struct mbuf *); /* * Initialize the list of tags associated with an mbuf. */ static __inline void m_tag_init(struct mbuf *m) { SLIST_INIT(&m->m_pkthdr.tags); } /* * Set up the contents of a tag. Note that this does not fill in the free * method; the caller is expected to do that. * * XXX probably should be called m_tag_init, but that was already taken. */ static __inline void m_tag_setup(struct m_tag *t, u_int32_t cookie, int type, int len) { t->m_tag_id = type; t->m_tag_len = len; t->m_tag_cookie = cookie; } /* * Reclaim resources associated with a tag. */ static __inline void m_tag_free(struct m_tag *t) { (*t->m_tag_free)(t); } /* * Return the first tag associated with an mbuf. */ static __inline struct m_tag * m_tag_first(struct mbuf *m) { return (SLIST_FIRST(&m->m_pkthdr.tags)); } /* * Return the next tag in the list of tags associated with an mbuf. */ static __inline struct m_tag * m_tag_next(struct mbuf *m, struct m_tag *t) { return (SLIST_NEXT(t, m_tag_link)); } /* * Prepend a tag to the list of tags associated with an mbuf. */ static __inline void m_tag_prepend(struct mbuf *m, struct m_tag *t) { SLIST_INSERT_HEAD(&m->m_pkthdr.tags, t, m_tag_link); } /* * Unlink a tag from the list of tags associated with an mbuf. */ static __inline void m_tag_unlink(struct mbuf *m, struct m_tag *t) { SLIST_REMOVE(&m->m_pkthdr.tags, t, m_tag, m_tag_link); } /* These are for OpenBSD compatibility. */ #define MTAG_ABI_COMPAT 0 /* compatibility ABI */ static __inline struct m_tag * m_tag_get(int type, int length, int wait) { return (m_tag_alloc(MTAG_ABI_COMPAT, type, length, wait)); } static __inline struct m_tag * m_tag_find(struct mbuf *m, int type, struct m_tag *start) { return (SLIST_EMPTY(&m->m_pkthdr.tags) ? (struct m_tag *)NULL : m_tag_locate(m, MTAG_ABI_COMPAT, type, start)); } static __inline struct mbuf * m_free(struct mbuf *m) { struct mbuf *n = m->m_next; if ((m->m_flags & (M_PKTHDR|M_NOFREE)) == (M_PKTHDR|M_NOFREE)) m_tag_delete_chain(m, NULL); if (m->m_flags & M_EXT) mb_free_ext(m); else if ((m->m_flags & M_NOFREE) == 0) uma_zfree(zone_mbuf, m); return (n); } static int inline rt_m_getfib(struct mbuf *m) { KASSERT(m->m_flags & M_PKTHDR , ("Attempt to get FIB from non header mbuf.")); return (m->m_pkthdr.fibnum); } #define M_GETFIB(_m) rt_m_getfib(_m) #define M_SETFIB(_m, _fib) do { \ KASSERT((_m)->m_flags & M_PKTHDR, ("Attempt to set FIB on non header mbuf.")); \ ((_m)->m_pkthdr.fibnum) = (_fib); \ } while (0) #endif /* _KERNEL */ #ifdef MBUF_PROFILING void m_profile(struct mbuf *m); #define M_PROFILE(m) m_profile(m) #else #define M_PROFILE(m) #endif struct mbufq { STAILQ_HEAD(, mbuf) mq_head; int mq_len; int mq_maxlen; }; static inline void mbufq_init(struct mbufq *mq, int maxlen) { STAILQ_INIT(&mq->mq_head); mq->mq_maxlen = maxlen; mq->mq_len = 0; } static inline struct mbuf * mbufq_flush(struct mbufq *mq) { struct mbuf *m; m = STAILQ_FIRST(&mq->mq_head); STAILQ_INIT(&mq->mq_head); mq->mq_len = 0; return (m); } static inline void mbufq_drain(struct mbufq *mq) { struct mbuf *m, *n; n = mbufq_flush(mq); while ((m = n) != NULL) { n = m->m_nextpkt; m_freem(m); } } static inline struct mbuf * mbufq_first(struct mbufq *mq) { return (STAILQ_FIRST(&mq->mq_head)); } static inline struct mbuf * mbufq_last(struct mbufq *mq) { return (STAILQ_LAST(&mq->mq_head, mbuf, m_stailqpkt)); } static inline int mbufq_full(struct mbufq *mq) { return (mq->mq_len >= mq->mq_maxlen); } static inline int mbufq_len(struct mbufq *mq) { return (mq->mq_len); } static inline int mbufq_enqueue(struct mbufq *mq, struct mbuf *m) { if (mbufq_full(mq)) return (ENOBUFS); STAILQ_INSERT_TAIL(&mq->mq_head, m, m_stailqpkt); mq->mq_len++; return (0); } static inline struct mbuf * mbufq_dequeue(struct mbufq *mq) { struct mbuf *m; m = STAILQ_FIRST(&mq->mq_head); if (m) { STAILQ_REMOVE_HEAD(&mq->mq_head, m_stailqpkt); mq->mq_len--; } return (m); } static inline void mbufq_prepend(struct mbufq *mq, struct mbuf *m) { STAILQ_INSERT_HEAD(&mq->mq_head, m, m_stailqpkt); mq->mq_len++; } #endif /* !_SYS_MBUF_H_ */ Index: projects/ifnet/sys/sys/module.h =================================================================== --- projects/ifnet/sys/sys/module.h (revision 277209) +++ projects/ifnet/sys/sys/module.h (revision 277210) @@ -1,218 +1,219 @@ /*- * Copyright (c) 1997 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS_MODULE_H_ #define _SYS_MODULE_H_ /* * Module metadata types */ #define MDT_DEPEND 1 /* argument is a module name */ #define MDT_MODULE 2 /* module declaration */ #define MDT_VERSION 3 /* module version(s) */ +#define MDT_PNP_INFO 4 /* Plug and play hints record */ #define MDT_STRUCT_VERSION 1 /* version of metadata structure */ #define MDT_SETNAME "modmetadata_set" typedef enum modeventtype { MOD_LOAD, MOD_UNLOAD, MOD_SHUTDOWN, MOD_QUIESCE } modeventtype_t; typedef struct module *module_t; typedef int (*modeventhand_t)(module_t, int /* modeventtype_t */, void *); /* * Struct for registering modules statically via SYSINIT. */ typedef struct moduledata { const char *name; /* module name */ modeventhand_t evhand; /* event handler */ void *priv; /* extra data */ } moduledata_t; /* * A module can use this to report module specific data to the user via * kldstat(2). */ typedef union modspecific { int intval; u_int uintval; long longval; u_long ulongval; } modspecific_t; /* * Module dependency declarartion */ struct mod_depend { int md_ver_minimum; int md_ver_preferred; int md_ver_maximum; }; /* * Module version declaration */ struct mod_version { int mv_version; }; struct mod_metadata { int md_version; /* structure version MDTV_* */ int md_type; /* type of entry MDT_* */ void *md_data; /* specific data */ const char *md_cval; /* common string label */ }; #ifdef _KERNEL #include #define MODULE_METADATA(uniquifier, type, data, cval) \ static struct mod_metadata _mod_metadata##uniquifier = { \ MDT_STRUCT_VERSION, \ type, \ data, \ cval \ }; \ DATA_SET(modmetadata_set, _mod_metadata##uniquifier) #define MODULE_DEPEND(module, mdepend, vmin, vpref, vmax) \ static struct mod_depend _##module##_depend_on_##mdepend = { \ vmin, \ vpref, \ vmax \ }; \ MODULE_METADATA(_md_##module##_on_##mdepend, MDT_DEPEND, \ &_##module##_depend_on_##mdepend, #mdepend) /* * Every kernel has a 'kernel' module with the version set to * __FreeBSD_version. We embed a MODULE_DEPEND() inside every module * that depends on the 'kernel' module. It uses the current value of * __FreeBSD_version as the minimum and preferred versions. For the * maximum version it rounds the version up to the end of its branch * (i.e. M99999 for M.x). This allows a module built on M.x to work * on M.y systems where y >= x, but fail on M.z systems where z < x. */ #define MODULE_KERNEL_MAXVER (roundup(__FreeBSD_version, 100000) - 1) #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); \ SYSINIT(name##module, sub, order, module_register_init, &data); \ struct __hack #define DECLARE_MODULE(name, data, sub, order) \ DECLARE_MODULE_WITH_MAXVER(name, data, sub, order, MODULE_KERNEL_MAXVER) /* * The module declared with DECLARE_MODULE_TIED can only be loaded * into the kernel with exactly the same __FreeBSD_version. * * Use it for modules that use kernel interfaces that are not stable * even on STABLE/X branches. */ #define DECLARE_MODULE_TIED(name, data, sub, order) \ DECLARE_MODULE_WITH_MAXVER(name, data, sub, order, __FreeBSD_version) #define MODULE_VERSION(module, version) \ static struct mod_version _##module##_version = { \ version \ }; \ MODULE_METADATA(_##module##_version, MDT_VERSION, \ &_##module##_version, #module) extern struct sx modules_sx; #define MOD_XLOCK sx_xlock(&modules_sx) #define MOD_SLOCK sx_slock(&modules_sx) #define MOD_XUNLOCK sx_xunlock(&modules_sx) #define MOD_SUNLOCK sx_sunlock(&modules_sx) #define MOD_LOCK_ASSERT sx_assert(&modules_sx, SX_LOCKED) #define MOD_XLOCK_ASSERT sx_assert(&modules_sx, SX_XLOCKED) struct linker_file; void module_register_init(const void *); int module_register(const struct moduledata *, struct linker_file *); module_t module_lookupbyname(const char *); module_t module_lookupbyid(int); int module_quiesce(module_t); void module_reference(module_t); void module_release(module_t); int module_unload(module_t); int module_getid(module_t); module_t module_getfnext(module_t); const char * module_getname(module_t); void module_setspecific(module_t, modspecific_t *); struct linker_file *module_file(module_t); #ifdef MOD_DEBUG extern int mod_debug; #define MOD_DEBUG_REFS 1 #define MOD_DPF(cat, args) do { \ if (mod_debug & MOD_DEBUG_##cat) \ printf(args); \ } while (0) #else /* !MOD_DEBUG */ #define MOD_DPF(cat, args) #endif #endif /* _KERNEL */ #define MAXMODNAME 32 struct module_stat { int version; /* set to sizeof(struct module_stat) */ char name[MAXMODNAME]; int refs; int id; modspecific_t data; }; #ifndef _KERNEL #include __BEGIN_DECLS int modnext(int _modid); int modfnext(int _modid); int modstat(int _modid, struct module_stat *_stat); int modfind(const char *_name); __END_DECLS #endif #endif /* !_SYS_MODULE_H_ */ Index: projects/ifnet/sys =================================================================== --- projects/ifnet/sys (revision 277209) +++ projects/ifnet/sys (revision 277210) Property changes on: projects/ifnet/sys ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys:r277180-277209 Index: projects/ifnet/usr.sbin/kldxref/kldxref.c =================================================================== --- projects/ifnet/usr.sbin/kldxref/kldxref.c (revision 277209) +++ projects/ifnet/usr.sbin/kldxref/kldxref.c (revision 277210) @@ -1,369 +1,373 @@ /* * Copyright (c) 2000, Boris Popov * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Boris Popov. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #define FREEBSD_ELF #include #include #include #include #include #include #include #include #include "ef.h" #define MAXRECSIZE 8192 #define check(val) if ((error = (val)) != 0) break static int dflag; /* do not create a hint file, only write on stdout */ static int verbose; static FILE *fxref; /* current hints file */ static const char *xref_file = "linker.hints"; /* * A record is stored in the static buffer recbuf before going to disk. */ static char recbuf[MAXRECSIZE]; static int recpos; /* current write position */ static int reccnt; /* total record written to this file so far */ static void intalign(void) { recpos = (recpos + sizeof(int) - 1) & ~(sizeof(int) - 1); } static void record_start(void) { recpos = 0; memset(recbuf, 0, MAXRECSIZE); } static int record_end(void) { if (recpos == 0) return 0; reccnt++; intalign(); fwrite(&recpos, sizeof(recpos), 1, fxref); return fwrite(recbuf, recpos, 1, fxref) != 1 ? errno : 0; } static int record_buf(const void *buf, int size) { if (MAXRECSIZE - recpos < size) errx(1, "record buffer overflow"); memcpy(recbuf + recpos, buf, size); recpos += size; return 0; } /* * An int is stored in host order and aligned */ static int record_int(int val) { intalign(); return record_buf(&val, sizeof(val)); } /* * A string is stored as 1-byte length plus data, no padding */ static int record_string(const char *str) { int len, error; u_char val; if (dflag) return 0; val = len = strlen(str); if (len > 255) errx(1, "string %s too long", str); error = record_buf(&val, sizeof(val)); if (error) return error; return record_buf(str, len); } static int parse_entry(struct mod_metadata *md, const char *cval, struct elf_file *ef, const char *kldname) { struct mod_depend mdp; struct mod_version mdv; Elf_Off data = (Elf_Off)md->md_data; int error = 0; record_start(); switch (md->md_type) { case MDT_DEPEND: if (!dflag) break; check(EF_SEG_READ(ef, data, sizeof(mdp), &mdp)); printf(" depends on %s.%d (%d,%d)\n", cval, mdp.md_ver_preferred, mdp.md_ver_minimum, mdp.md_ver_maximum); break; case MDT_VERSION: check(EF_SEG_READ(ef, data, sizeof(mdv), &mdv)); if (dflag) { printf(" interface %s.%d\n", cval, mdv.mv_version); } else { record_int(MDT_VERSION); record_string(cval); record_int(mdv.mv_version); record_string(kldname); } break; case MDT_MODULE: if (dflag) { printf(" module %s\n", cval); } else { record_int(MDT_MODULE); record_string(cval); record_string(kldname); } break; + case MDT_PNP_INFO: + if (dflag) { + printf(" pnp info for bus %s\n", cval); + } default: warnx("unknown metadata record %d in file %s", md->md_type, kldname); } if (!error) record_end(); return error; } static int read_kld(char *filename, char *kldname) { struct mod_metadata md; struct elf_file ef; void **p, **orgp; int error, eftype, nmlen; long start, finish, entries; char kldmodname[MAXMODNAME + 1], cval[MAXMODNAME + 1], *cp; if (verbose || dflag) printf("%s\n", filename); error = ef_open(filename, &ef, verbose); if (error) { error = ef_obj_open(filename, &ef, verbose); if (error) { if (verbose) warnc(error, "elf_open(%s)", filename); return error; } } eftype = EF_GET_TYPE(&ef); if (eftype != EFT_KLD && eftype != EFT_KERNEL) { EF_CLOSE(&ef); return 0; } if (!dflag) { cp = strrchr(kldname, '.'); nmlen = (cp != NULL) ? cp - kldname : (int)strlen(kldname); if (nmlen > MAXMODNAME) nmlen = MAXMODNAME; strlcpy(kldmodname, kldname, nmlen); /* fprintf(fxref, "%s:%s:%d\n", kldmodname, kldname, 0);*/ } do { check(EF_LOOKUP_SET(&ef, MDT_SETNAME, &start, &finish, &entries)); check(EF_SEG_READ_ENTRY_REL(&ef, start, sizeof(*p) * entries, (void *)&p)); orgp = p; while(entries--) { check(EF_SEG_READ_REL(&ef, (Elf_Off)*p, sizeof(md), &md)); p++; check(EF_SEG_READ(&ef, (Elf_Off)md.md_cval, sizeof(cval), cval)); cval[MAXMODNAME] = '\0'; parse_entry(&md, cval, &ef, kldname); } if (error) warnc(error, "error while reading %s", filename); free(orgp); } while(0); EF_CLOSE(&ef); return error; } /* * Create a temp file in directory root, make sure we don't * overflow the buffer for the destination name */ static FILE * maketempfile(char *dest, const char *root) { char *p; int n, fd; p = strrchr(root, '/'); n = p != NULL ? p - root + 1 : 0; if (snprintf(dest, MAXPATHLEN, "%.*slhint.XXXXXX", n, root) >= MAXPATHLEN) { errno = ENAMETOOLONG; return NULL; } fd = mkstemp(dest); if (fd < 0) return NULL; fchmod(fd, 0644); /* nothing secret in the file */ return fdopen(fd, "w+"); } static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN]; static void usage(void) { fprintf(stderr, "%s\n", "usage: kldxref [-Rdv] [-f hintsfile] path ..." ); exit(1); } static int compare(const FTSENT *const *a, const FTSENT *const *b) { if ((*a)->fts_info == FTS_D && (*b)->fts_info != FTS_D) return 1; if ((*a)->fts_info != FTS_D && (*b)->fts_info == FTS_D) return -1; return strcmp((*a)->fts_name, (*b)->fts_name); } int main(int argc, char *argv[]) { FTS *ftsp; FTSENT *p; int opt, fts_options, ival; struct stat sb; fts_options = FTS_PHYSICAL; while ((opt = getopt(argc, argv, "Rdf:v")) != -1) { switch (opt) { case 'd': /* no hint file, only print on stdout */ dflag = 1; break; case 'f': /* use this name instead of linker.hints */ xref_file = optarg; break; case 'v': verbose++; break; case 'R': /* recurse on directories */ fts_options |= FTS_COMFOLLOW; break; default: usage(); /* NOTREACHED */ } } if (argc - optind < 1) usage(); argc -= optind; argv += optind; if (stat(argv[0], &sb) != 0) err(1, "%s", argv[0]); if ((sb.st_mode & S_IFDIR) == 0) { errno = ENOTDIR; err(1, "%s", argv[0]); } ftsp = fts_open(argv, fts_options, compare); if (ftsp == NULL) exit(1); for (;;) { p = fts_read(ftsp); if ((p == NULL || p->fts_info == FTS_D) && fxref) { /* close and rename the current hint file */ fclose(fxref); fxref = NULL; if (reccnt) { rename(tempname, xrefname); } else { /* didn't find any entry, ignore this file */ unlink(tempname); unlink(xrefname); } } if (p == NULL) break; if (p->fts_info == FTS_D && !dflag) { /* visiting a new directory, create a new hint file */ snprintf(xrefname, sizeof(xrefname), "%s/%s", ftsp->fts_path, xref_file); fxref = maketempfile(tempname, ftsp->fts_path); if (fxref == NULL) err(1, "can't create %s", tempname); ival = 1; fwrite(&ival, sizeof(ival), 1, fxref); reccnt = 0; } /* skip non-files or .symbols entries */ if (p->fts_info != FTS_F) continue; if (p->fts_namelen >= 8 && strcmp(p->fts_name + p->fts_namelen - 8, ".symbols") == 0) continue; read_kld(p->fts_path, p->fts_name); } fts_close(ftsp); return 0; } Index: projects/ifnet =================================================================== --- projects/ifnet (revision 277209) +++ projects/ifnet (revision 277210) Property changes on: projects/ifnet ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r277180-277209