qeth.c
来自「linux-2.4.29操作系统的源码」· C语言 代码 · 共 2,327 行 · 第 1/5 页
C
2,327 行
/* * * linux/drivers/s390/net/qeth.c ($Revision: 1.337.4.24 $) * * Linux on zSeries OSA Express and HiperSockets support * * Copyright 2000,2003 IBM Corporation * * Author(s): Utz Bacher <utz.bacher@de.ibm.com> * Cornelia Huck <cohuck@de.ibm.com> (chandev stuff, * numerous bugfixes) * Frank Pavlic <pavlic@de.ibm.com> (query/purge ARP, SNMP, fixes) * Andreas Herrmann <aherrman@de.ibm.com> (bugfixes) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *//* * For all devices, three channels must be available to the driver. One * channel is the read channel, one is the write channel and the third * one is the channel used to control QDIO. * * There are several stages from the channel recognition to the running * network device: * - The channels are scanned and ordered due to the parameters (see * MODULE_PARM_DESC) * - The card is hardsetup: this means, that the communication channels * are prepared * - The card is softsetup: this means, that commands are issued * to activate the network parameters * - After that, data can flow through the card (transported by QDIO) * *IPA Takeover: * /proc/qeth_ipa_takeover provides the possibility to add and remove * certain ranges of IP addresses to the driver. As soon as these * addresses have to be set by the driver, the driver uses the OSA * Address Takeover mechanism. * reading out of the proc-file displays the registered addresses; * writing into it changes the information. Only one command at one * time must be written into the file. Subsequent commands are ignored. * The following commands are available: * inv4 * inv6 * add4 <ADDR>/<mask bits>[:<interface>] * add6 <ADDR>/<mask bits>[:<interface>] * del4 <ADDR>/<mask bits>[:<interface>] * del6 <ADDR>/<mask bits>[:<interface>] * inv4 and inv6 toggle the IPA takeover behaviour for all interfaces: * when inv4 was input once, all addresses specified with add4 are not * set using the takeover mechanism, but all other IPv4 addresses are set so. * * add# adds an address range, del# deletes an address range. # corresponds * to the IP version (4 or 6). * <ADDR> is a 8 or 32byte hexadecimal view of the IP address. * <mask bits> specifies the number of bits which are set in the network mask. * <interface> is optional and specifies the interface name to which the * address range is bound. * E. g. * add4 C0a80100/24 * activates all addresses in the 192.168.10 subnet for address takeover. * Note, that the address is not taken over before an according ifconfig * is executed. * *VIPA: * add_vipa4 <ADDR>:<interface> * add_vipa6 <ADDR>:<interface> * del_vipa4 <ADDR>:<interface> * del_vipa6 <ADDR>:<interface> * * the specified address is set/unset as VIPA on the specified interface. * use the src_vipa package to exploit this out of arbitrary applications. * *Proxy ARP: * * add_rxip4 <ADDR>:<interface> * add_rxip6 <ADDR>:<interface> * del_rxip4 <ADDR>:<interface> * del_rxip6 <ADDR>:<interface> * * the specified address is set/unset as "do not fail a gratuitous ARP" * on the specified interface. this can be used to act as a proxy ARP. */void volatile qeth_eyecatcher(void){ return;}#include <linux/config.h>#ifndef CONFIG_CHANDEV#error "qeth can only be compiled with chandev support"#endif /* CONFIG_CHANDEV */#include <linux/module.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/mm.h>#include <linux/version.h>#include <asm/io.h>#include <asm/ebcdic.h>#include <linux/ctype.h>#include <asm/semaphore.h>#include <linux/if.h>#include <linux/if_arp.h>#include <linux/ip.h>#include <linux/inetdevice.h>#include <linux/netdevice.h>#include <linux/sched.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/tcp.h>#include <linux/icmp.h>#include <linux/skbuff.h>#ifdef CONFIG_PROC_FS#include <linux/proc_fs.h>#endif /* CONFIG_PROC_FS */#include <net/route.h>#include <net/arp.h>#include <linux/in.h>#include <linux/igmp.h>#include <net/ip.h>#include <asm/uaccess.h>#include <linux/init.h>#include <net/ipv6.h>#include <linux/in6.h>#include <net/if_inet6.h>#include <net/addrconf.h>#include <linux/if_tr.h>#include <linux/trdevice.h>#include <linux/etherdevice.h>#include <linux/reboot.h>#include <linux/if_vlan.h>#include <asm/chandev.h>#include <asm/irq.h>#include <asm/s390dyn.h>#include <asm/debug.h>#include <asm/processor.h>#include <asm/qdio.h>#include "qeth_mpc.h"#include "qeth.h"/****************** MODULE PARAMETER VARIABLES ********************/static int qeth_sparebufs=0;MODULE_PARM(qeth_sparebufs,"i");MODULE_PARM_DESC(qeth_sparebufs,"the number of pre-allocated spare buffers " \ "reserved for low memory situations");static int global_stay_in_mem=0;/****************** MODULE STUFF **********************************/#define VERSION_QETH_C "$Revision: 1.337.4.24 $"static const char *version="qeth S/390 OSA-Express driver (" \ VERSION_QETH_C "/" VERSION_QETH_H "/" VERSION_QETH_MPC_H QETH_VERSION_IPV6 QETH_VERSION_VLAN ")";MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");MODULE_DESCRIPTION("Linux on zSeries OSA Express and HiperSockets support\n" \ "Copyright 2000,2003 IBM Corporation\n");MODULE_LICENSE("GPL");/******************** HERE WE GO ***********************************/static qeth_card_t *firstcard=NULL;static sparebufs_t sparebufs[MAX_SPARE_BUFFERS];static int sparebuffer_count;static unsigned int known_devices[][10]=QETH_MODELLIST_ARRAY;static spinlock_t setup_lock;static rwlock_t list_lock=RW_LOCK_UNLOCKED;static debug_info_t *qeth_dbf_setup=NULL;static debug_info_t *qeth_dbf_data=NULL;static debug_info_t *qeth_dbf_misc=NULL;static debug_info_t *qeth_dbf_control=NULL;static debug_info_t *qeth_dbf_trace=NULL;static debug_info_t *qeth_dbf_sense=NULL;static debug_info_t *qeth_dbf_qerr=NULL;static int proc_file_registration;#ifdef QETH_PERFORMANCE_STATSstatic int proc_perf_file_registration;#define NOW qeth_get_micros()#endif /* QETH_PERFORMANCE_STATS */static int proc_ipato_file_registration;static int ipato_inv4=0,ipato_inv6=0;static ipato_entry_t *ipato_entries=NULL;static spinlock_t ipato_list_lock;typedef struct { char *data; int len;} tempinfo_t;/* thought I could get along without forward declarations... * just lazyness here */static int qeth_reinit_thread(void*);static inline void qeth_schedule_recovery(qeth_card_t *card);static int qeth_fake_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len);static inline int QETH_IP_VERSION(struct sk_buff *skb){ switch (skb->protocol) { case ETH_P_IPV6: return 6; case ETH_P_IP: return 4; default: return 0; }}/* not a macro, as one of the arguments is atomic_read */static inline int qeth_min(int a,int b){ if (a<b) return a; else return b;}/* * This is our local skb_unshare, only with pskb_copy instead of skb_copy. * We place our headers whare Ethernet MAC was, so we do not need * full skb_copy. */static inline struct sk_buff *qeth_pskb_unshare(struct sk_buff *skb, int pri){ struct sk_buff *nskb; if (!skb_cloned(skb)) return skb; nskb = skb_copy(skb, pri); kfree_skb(skb); /* free our shared copy */ return nskb;}static inline unsigned int qeth_get_millis(void){ __u64 time; asm volatile ("STCK %0" : "=m" (time)); return (int) (time>>22); /* time>>12 is microseconds, we divide it by 1024 */}#ifdef QETH_PERFORMANCE_STATSstatic inline unsigned int qeth_get_micros(void){ __u64 time; asm volatile ("STCK %0" : "=m" (time)); return (int) (time>>12);}#endif /* QETH_PERFORMANCE_STATS */static void qeth_delay_millis(unsigned long msecs){ unsigned int start; start=qeth_get_millis(); while (qeth_get_millis()-start<msecs) ;}static void qeth_wait_nonbusy(unsigned int timeout){ unsigned int start; char dbf_text[15]; sprintf(dbf_text,"wtnb%4x",timeout); QETH_DBF_TEXT3(0,trace,dbf_text); start=qeth_get_millis(); for (;;) { set_task_state(current,TASK_INTERRUPTIBLE); if (qeth_get_millis()-start>timeout) { goto out; } schedule_timeout(((start+timeout-qeth_get_millis())>>10)*HZ); } out: set_task_state(current,TASK_RUNNING);}static inline void qeth_get_mac_for_ipm(__u32 ipm,char *mac, struct net_device *dev) { if (dev->type==ARPHRD_IEEE802_TR) ip_tr_mc_map(ipm,mac); else ip_eth_mc_map(ipm,mac);}#define HEXDUMP16(importance,header,ptr) \PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \ "%02x %02x %02x %02x %02x %02x %02x %02x\n", \ *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \ *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \ *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \ *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \ *(((char*)ptr)+12),*(((char*)ptr)+13), \ *(((char*)ptr)+14),*(((char*)ptr)+15)); \PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \ "%02x %02x %02x %02x %02x %02x %02x %02x\n", \ *(((char*)ptr)+16),*(((char*)ptr)+17), \ *(((char*)ptr)+18),*(((char*)ptr)+19), \ *(((char*)ptr)+20),*(((char*)ptr)+21), \ *(((char*)ptr)+22),*(((char*)ptr)+23), \ *(((char*)ptr)+24),*(((char*)ptr)+25), \ *(((char*)ptr)+26),*(((char*)ptr)+27), \ *(((char*)ptr)+28),*(((char*)ptr)+29), \ *(((char*)ptr)+30),*(((char*)ptr)+31));#define atomic_swap(a,b) xchg((int*)a.counter,b)#ifdef QETH_DBF_LIKE_HELL#define my_read_lock(x) do { \ void *ptr=x; \ QETH_DBF_TEXT6(0,trace,"rd_lck"); \ QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \ read_lock(x); \} while (0)#define my_read_unlock(x) do { \ void *ptr=x; \ QETH_DBF_TEXT6(0,trace,"rd_unlck"); \ QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \ read_unlock(x); \} while (0)#define my_write_lock(x) do { \ void *ptr=x; \ QETH_DBF_TEXT6(0,trace,"wr_lck"); \ QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \ write_lock(x); \} while (0)#define my_write_unlock(x) do { \ void *ptr=x; \ QETH_DBF_TEXT6(0,trace,"wr_unlck"); \ QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \ write_unlock(x); \} while (0)#define my_spin_lock(x) do { \ void *ptr=x; \ QETH_DBF_TEXT6(0,trace,"sp_lck"); \ QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \ spin_lock(x); \} while (0)#define my_spin_unlock(x) do { \ void *ptr=x; \ QETH_DBF_TEXT6(0,trace,"sp_unlck"); \ QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \ spin_unlock(x); \} while (0)#define my_spin_lock_irqsave(x,y) do { \ void *ptr=x; \ QETH_DBF_TEXT6(0,trace,"sp_lck_i"); \ QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \ spin_lock_irqsave(x,y); \} while (0)#define my_spin_unlock_irqrestore(x,y) do { \ void *ptr=x; \ QETH_DBF_TEXT6(0,trace,"sp_nlk_i"); \ QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \ spin_unlock_irqrestore(x,y); \} while (0)#else /* QETH_DBF_LIKE_HELL */#define my_read_lock(x) read_lock(x)#define my_write_lock(x) write_lock(x)#define my_read_unlock(x) read_unlock(x)#define my_write_unlock(x) write_unlock(x)#define my_spin_lock(x) spin_lock(x)#define my_spin_unlock(x) spin_unlock(x)#define my_spin_lock_irqsave(x,y) spin_lock_irqsave(x,y)#define my_spin_unlock_irqrestore(x,y) spin_unlock_irqrestore(x,y)#endif /* QETH_DBF_LIKE_HELL */static int inline my_spin_lock_nonbusy(qeth_card_t *card,spinlock_t *lock){ for (;;) { if (card) { if (atomic_read(&card->shutdown_phase)) return -1; } if (spin_trylock(lock)) return 0; qeth_wait_nonbusy(QETH_IDLE_WAIT_TIME); }}#ifdef CONFIG_ARCH_S390X#define QETH_GET_ADDR(x) ((__u32)(unsigned long)x)#else /* CONFIG_ARCH_S390X */#define QETH_GET_ADDR(x) ((__u32)x)#endif /* CONFIG_ARCH_S390X */static inline int qeth_does_card_exist(qeth_card_t *card){ qeth_card_t *c=firstcard; int rc=0; my_read_lock(&list_lock); while (c) { if (c==card) { rc=1; break; } c=c->next; } my_read_unlock(&list_lock); return rc;}static inline qeth_card_t *qeth_get_card_by_irq(int irq){ qeth_card_t *card; my_read_lock(&list_lock); card=firstcard; while (card) { if ((card->irq0==irq)&& (atomic_read(&card->shutdown_phase)!= QETH_REMOVE_CARD_QUICK)) break; if ((card->irq1==irq)&& (atomic_read(&card->shutdown_phase)!= QETH_REMOVE_CARD_QUICK)) break; if ((card->irq2==irq)&& (atomic_read(&card->shutdown_phase)!= QETH_REMOVE_CARD_QUICK)) break; card=card->next; } my_read_unlock(&list_lock); return card;}static int qeth_getxdigit(char c){ if ((c>='0') && (c<='9')) return c-'0'; if ((c>='a') && (c<='f')) return c+10-'a'; if ((c>='A') && (c<='F')) return c+10-'A'; return -1;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?