#include #include #include #include #include #include #include #include #include #include #include #include "sn.h" #include "scsinet_arp.h" #if 0 #define SCSINET_DEBUG #endif #ifdef SCSINET_DEBUG # ifdef ENTER # undef ENTER # endif # ifdef EXIT # undef EXIT # endif # ifdef PKT # undef PKT # endif # ifdef DEBUG # undef DEBUG # endif # define ENTER printk("SCNET: entering %s \n",__FUNCTION__); # define EXIT printk("SCNET: exit %s\n",__FUNCTION__); # define PKT printk("SCNET: line: %u\n",__LINE__); # define DEBUG(x) x #else # ifdef ENTER # undef ENTER # endif # ifdef EXIT # undef EXIT # endif # ifdef PKT # undef PKT # endif # ifdef DEBUG # undef DEBUG # endif # define ENTER # define EXIT # define DEBUG(x) # define PKT /* # define PKT */ #endif int scsinet_net_init(struct net_device *dev); int scsinet_net_open(struct net_device *dev); int scsinet_net_stop(struct net_device *dev); void scsinet_real_net_tx(void *); int scsinet_net_tx(struct sk_buff *skb,struct net_device *dev); void scsinet_net_tx_finish(unsigned char *); void scsinet_net_timeout(struct net_device *dev); int scsinet_scsi_init(void); int scsinet_scsi_exit(void); int scsinet_scsi_attach(int device); int scsinet_scsi_detach(int device); int scsinet_scsi_rx_packet(int len,unsigned char *data); void scsinet_token_timer(unsigned long ptr); struct net_device_stats *scsinet_net_getstats(struct net_device *dev); static int scsinet_proc_info(char *page,char **start,off_t offset,int count,int *eof,void *data); /* ETH interface */ struct scsinet_netpriv { struct net_device_stats stats; char status; spinlock_t lock; }; struct scsinet_devices_struct { char present; int id; }; struct scsinet_devices_struct *scsinet_devices; struct net_device *scsinet_net_device; #define SETNP struct scsinet_netpriv *np=(struct scsinet_netpriv *)dev->priv #define SETNP2 struct scsinet_netpriv *np=(struct scsinet_netpriv *)scsinet_net_device->priv /* SKB list */ struct skb_list_struct { struct list_head list; struct sk_buff *skb; char inuse; }; /* static struct skb_list_struct *skb_list; */ #define SKBLISTNUM 100 struct skb_list_struct *skb_list; struct skb_list_struct *skb_endlist; static int numused; #define FREE 0 #define USED 1 static struct tq_struct scsinet_schedule_task; /* token */ struct timer_list token_timer; static unsigned char *token; static struct timeval last_token; struct timer_list token_fail_timer; static char got_token; static int token_counter; static int last_count; static int new_count; static int got_count; /* sn interface */ struct sn_upper scsinet_sn = { name:"scsinet", init:scsinet_scsi_init, exit:scsinet_scsi_exit, attach:scsinet_scsi_attach, detach:scsinet_scsi_detach, rx_packet:scsinet_scsi_rx_packet, tx_finish:scsinet_net_tx_finish, }; /* debugging and timing */ struct proc_info_struct { unsigned int fast_token; unsigned int token_received; unsigned int token_sent; unsigned int sent; unsigned int confirmed; unsigned int list_error; long token_timer; long token_counter; long rx_timer; long rx_counter; long tx_timer; long tx_counter; long final_timer; long final_counter; }; long getTimeDiff(struct timeval begin,struct timeval end) { if (end.tv_sec>begin.tv_sec) return (1000000-begin.tv_usec)+end.tv_usec; return end.tv_usec-begin.tv_usec; } #define BEGINTIMER struct timeval begin,end; \ do_gettimeofday(&begin); #define ENDTIMER(x) do_gettimeofday(&end); \ x+=getTimeDiff(begin,end); static struct proc_info_struct *proc_info; static struct timeval last_token; /* SCSI functions */ int scsinet_scsi_init(void) { ENTER; scsinet_devices=(struct scsinet_devices_struct *)kmalloc(sizeof(struct scsinet_devices_struct)*16,GFP_KERNEL); if (scsinet_devices==NULL) { printk("scsinet: no more memory...\n"); return -ENOMEM; } memset(scsinet_devices,0,sizeof(struct scsinet_devices_struct)*16); EXIT; return 0; } int scsinet_scsi_exit(void) { ENTER; kfree(scsinet_devices); EXIT; return 0; } int scsinet_scsi_attach(int device) { ENTER; scsinet_devices[device].present=1; scsinet_devices[device].id=device; EXIT; return 0; } int scsinet_scsi_detach(int device) { ENTER; scsinet_devices[device].present=0; EXIT; return 0; } void scsinet_token_timer_sched(void *data) { scsinet_token_timer(FREE); } void scsinet_token_timer(unsigned long ptr) { int dev=sn_getmyid(); SETNP2; BEGINTIMER; if (new_count==0) { token[1]='6'; proc_info->token_sent++; } else { token[1]='7'; proc_info->fast_token++; } got_token=0; if (ptr==FREE) del_timer(&token_timer); if (np->status==0) return; do { dev+=1; if (dev==16) dev=0; if (dev==sn_getmyid()) { token_timer.expires=jiffies+2; add_timer(&token_timer); return; } } while (scsinet_devices[dev].present!=1); sn_tx_packet(dev,4,token); proc_info->token_sent++; ENDTIMER(proc_info->token_timer); proc_info->token_counter+=1; } int scsinet_scsi_rx_packet(int len,unsigned char *data) { SETNP2; struct sk_buff *skb; BEGINTIMER; /* token code */ if (data[0]=='1') { last_count=new_count; new_count=0; if (data[1]=='7') { /* got token */ got_count+=1; do_gettimeofday(&last_token); proc_info->fast_token++; token_counter=0; /* if (numused==0) { scsinet_schedule_task.routine=scsinet_token_timer_sched; scsinet_schedule_task.data=NULL; schedule_task(&scsinet_schedule_task); return 0; } */ scsinet_schedule_task.routine=scsinet_real_net_tx; scsinet_schedule_task.data=NULL; schedule_task(&scsinet_schedule_task); token_timer.expires=jiffies+1; /* 10 ms */ token_timer.data=USED; /* add_timer(&token_timer); */ got_token=1; return 0; } if (data[1]=='6') { scsinet_schedule_task.routine=scsinet_real_net_tx; scsinet_schedule_task.data=NULL; schedule_task(&scsinet_schedule_task); token_timer.expires=jiffies+1; /* 10 ms */ token_timer.data=USED; add_timer(&token_timer); got_token=2; } return 0; } got_count+=1; skb=dev_alloc_skb(len+5); if (skb==NULL) { printk("Memory squezze, dropping packet...\n"); np->stats.rx_dropped++; return 0; } /* skb_reserve(skb,2); */ memcpy(skb_put(skb,len),data,len); skb->dev=scsinet_net_device; skb->protocol=eth_type_trans(skb,scsinet_net_device); skb->pkt_type=PACKET_HOST; skb->dev->last_rx=jiffies; netif_rx(skb); np->stats.rx_packets++; np->stats.rx_bytes+=len; ENDTIMER(proc_info->rx_timer); proc_info->rx_counter+=1; return 0; } /* eth functions */ int scsinet_net_init(struct net_device *dev) { struct scsinet_netpriv *np; ENTER; memset(dev->dev_addr,0,ETH_ALEN); dev->dev_addr[0]=(char)sn_getmyid(); if (dev->priv==NULL) kfree(dev->priv); dev->priv=kmalloc(sizeof(struct scsinet_netpriv),GFP_KERNEL); if (dev->priv==NULL) return -ENOMEM; memset(dev->priv,0,sizeof(struct scsinet_netpriv)); np=(struct scsinet_netpriv *)dev->priv; np->lock=SPIN_LOCK_UNLOCKED; dev->open=scsinet_net_open; dev->stop=scsinet_net_stop; dev->hard_start_xmit=scsinet_net_tx; dev->get_stats=scsinet_net_getstats; dev->tx_timeout=scsinet_net_timeout; ether_setup(dev); /* this creates the network interface as a ethernet... */ dev->mtu=3000; dev->tx_queue_len=100; /* optimize this for scsi with token */ dev->watchdog_timeo=5*HZ; scsinet_net_device=dev; EXIT; return 0; } int scsinet_net_open(struct net_device *dev) { SETNP2; ENTER; MOD_INC_USE_COUNT; netif_start_queue(dev); np->status=1; do_gettimeofday(&last_token); if (sn_getmyid()==7) { printk("Starting token\n"); /* this must be done with wait queues */ /* init_timer(&token_fail_timer); token_fail_timer.function=scsinet_fail_token; token_fail_timer.expires=jiffies+HZ; add_timer(&token_fail_timer); */ scsinet_token_timer(FREE); }; EXIT; return 0; } int scsinet_net_stop(struct net_device *dev) { SETNP2; ENTER; netif_stop_queue(dev); np->status=0; MOD_DEC_USE_COUNT; EXIT; return 0; } int scsinet_send(struct sk_buff *skb) { int i; BEGINTIMER; PKT; if (skb==NULL) return 0; if ( ( skb->len % 2 ) == 1 ) { /* printk("%s:%u\n",__FUNCTION__,__LINE__); tskb=skb_copy_expand(skb,0,1,GFP_ATOMIC); memcpy(skb_put(tskb,1),"\0",1); dev_kfree_skb_any(skb); skb=tskb; */ memcpy(skb_put(skb,1),"\0",1); } PKT; if ( ( skb->len % 2 ) == 1 ) printk("Opps...SKB is odd, not even... not right. please the check. fuck you...\n"); PKT; if (skb->data[0]==255) { for (i=0;i<16;i+=1) if (scsinet_devices[i].present==1) sn_tx_packet(scsinet_devices[i].id,skb->len,skb->data); } else sn_tx_packet((int)skb->data[0],skb->len,skb->data); new_count+=1; PKT; scsinet_net_device->trans_start = jiffies; proc_info->sent+=1; PKT; ENDTIMER(proc_info->tx_timer); proc_info->tx_counter+=1; PKT; return 0; } int scsinet_net_tx(struct sk_buff *skb,struct net_device *dev) { int i; for (i=0;ilist_error+=1; netif_stop_queue(dev); return 0; } skb_list[i].inuse=USED; skb_list[i].skb=skb; dev->trans_start=jiffies; numused+=1; /* if (got_token==1) scsinet_real_net_tx(0); */ return 0; } long scsinet_timediff(struct timeval begin,struct timeval end) { long tmp; if (end.tv_sec==begin.tv_sec) { tmp=end.tv_usec-begin.tv_usec; return tmp; } if (end.tv_usec>begin.tv_usec) { tmp=end.tv_usec-begin.tv_usec; return tmp; } if (end.tv_usecdata==data) { break; } } else { proc_info->list_error+=1; } } } if (i==SKBLISTNUM) { proc_info->list_error+=1; return; } /* printk("%s:%u\n",__FUNCTION__,__LINE__); */ dev_kfree_skb_any(skb_endlist[i].skb); skb_endlist[i].inuse=FREE; PKT; if (netif_queue_stopped(scsinet_net_device)) netif_wake_queue(scsinet_net_device); proc_info->confirmed+=1; ENDTIMER(proc_info->final_timer); proc_info->final_counter+=1; } /* * stats function */ struct net_device_stats *scsinet_net_getstats(struct net_device *dev) { SETNP; return &np->stats; } void scsinet_net_timeout(struct net_device *dev) { ENTER; DEBUG(printk("SCNET: tx timeout in %s... oops...\n",dev->name)); EXIT; } int scsinet_proc_info(char *page,char **start,off_t offset,int count,int *eof,void *data) { int len=0; len+=sprintf(page,"SCNET proc info\n\tFast Token: %u\n\tToken Sent: %u\n\tToken received: %u\n\t" \ "Packets sent: %u\n\tPackets confirmed: %u\n\tList Errors: %u\n", proc_info->fast_token, proc_info->token_sent, proc_info->token_received, proc_info->sent, proc_info->confirmed, proc_info->list_error ); len+=sprintf(&page[len],"Profiling info:\n"); if (proc_info->token_counter!=0) len+=sprintf(&page[len],"\ttoken_timer: %u / %u\n",proc_info->token_timer,proc_info->token_counter); if (proc_info->rx_counter!=0) len+=sprintf(&page[len],"\trx_timer: %u / %u\n",proc_info->rx_timer,proc_info->rx_counter); if (proc_info->tx_counter!=0) len+=sprintf(&page[len],"\ttx_timer: %u / %u\n",proc_info->tx_timer,proc_info->tx_counter); if (proc_info->final_counter!=0) len+=sprintf(&page[len],"\tfinal_timer: %u / %u\n",proc_info->final_timer,proc_info->final_counter); return len; } static struct net_device scsinet_netdev; static int __init init_scsinet(void) { int err; ENTER; /* * Creates the list of sk_buff; */ got_token=0; token=kmalloc(4,GFP_KERNEL); if (token==NULL) return -ENOMEM; token[0]='1'; token[1]='6'; token[2]=0; token[3]=0; skb_list=kmalloc(sizeof(struct skb_list_struct)*SKBLISTNUM,GFP_KERNEL); if (skb_list==NULL) return -ENOMEM; skb_endlist=kmalloc(sizeof(struct skb_list_struct)*SKBLISTNUM,GFP_KERNEL); if (skb_endlist==NULL) { kfree(skb_list); return -ENOMEM; } memset(skb_list,0,sizeof(struct skb_list_struct)*SKBLISTNUM); memset(skb_endlist,0,sizeof(struct skb_list_struct)*SKBLISTNUM); numused=0; /* * register scsinet in eth */ scsinet_netdev.init=scsinet_net_init; err=dev_alloc_name(&scsinet_netdev,"sn%d"); if (err<0) goto err_dev_alloc; SET_MODULE_OWNER(&scsinet_netdev); if (register_netdev(&scsinet_netdev)!=0) { err=-EIO; goto err_reg_netdev; } if (sn_register_upper(&scsinet_sn)!=0) { err=-EIO; goto err_reg_sn; } init_timer(&token_timer); token_timer.function=scsinet_token_timer; token_timer.data=USED; token_timer.expires=jiffies+1; /* 10 ms */ /* proc */ proc_info=kmalloc(sizeof(struct proc_info_struct),GFP_KERNEL); if (proc_info==NULL) { printk("SCNET: no more memory\n"); err=-ENOMEM; goto err_reg_sn; } memset(proc_info,0,sizeof(struct proc_info_struct)); create_proc_read_entry("scnet", 0, NULL, scsinet_proc_info, NULL); EXIT; return 0; err_reg_sn: unregister_netdev(&scsinet_netdev); err_reg_netdev: err_dev_alloc: /* kfree(skb_list); */ return err; } static void __exit exit_scsinet(void) { int i; ENTER; /* * unregister scsinet in eth */ remove_proc_entry("scnet",NULL); del_timer_sync(&token_timer); /* if (sn_getmyid()==7) del_timer_sync(&token_fail_timer); */ unregister_netdev(&scsinet_netdev); sn_unregister_upper(&scsinet_sn); for (i=0;i