# HG changeset patch # User Louis Opter # Date 1350274652 -7200 # Node ID 4968acb39c7b341e1a02b7234c63b4bbba69c71c # Parent a9b47a2f8b9809fbd8cdb1ffa74b23a277dc502a Finalize work on minimal TX diff -r a9b47a2f8b98 -r 4968acb39c7b e1000_implement_the_frame_transmission_chunk.patch --- a/e1000_implement_the_frame_transmission_chunk.patch Thu Oct 04 19:33:43 2012 +0200 +++ b/e1000_implement_the_frame_transmission_chunk.patch Mon Oct 15 06:17:32 2012 +0200 @@ -1,6 +1,28 @@ # HG changeset patch # Parent 87ba2a19a59fb7be346ad40a57439b6b752b152e -rathaxes: start to queue up packets in the TX ring on the e1000 sample +rathaxes: add the transmission code for very simple packets in the e1000 sample + +Very simple packets are those which don't need to be fragmented nor checksummed. + +The transmission code is defined in different chunks/methods of the TxRing type. +An e1000::xmit method template has been added to call the different methods on +the TxRing object in the e1000::Context object. The interrupt handler has been +reworked to handle transmission interrupts and cleanup the TxRing accordingly. + +In parallel to the transmission code, the Socket::SKBuff type is now a real +Rathaxes type with its own debug and DMA operations methods. + +Also, sorry, this changeset is mixed with other changes: + +- Small improvements everywhere now that the compiler uses CNorm unstrict; +- Add the Device::AbstractDevice type; this Rathaxes represents the generic type + used by the kernel to represent a device; +- Add the Socket::AbstractSKBuff type too to represent the SKBuff type used by + the kernel; +- Likewise the Ethernet::AbstractDevice type represents the type used by the + kernel for Ethernet devices; +- Socket::SKBuff is now a real Rathaxes type which aggregates a KernelSKBuff + object. diff --git a/rathaxes/samples/e1000/CMakeLists.txt b/rathaxes/samples/e1000/CMakeLists.txt --- a/rathaxes/samples/e1000/CMakeLists.txt @@ -38,13 +60,13 @@ @@ -0,0 +1,25 @@ +with Device, LKM +{ -+ template type Device::Device() ++ template type Device::AbstractDevice() + { + chunk LKM::includes() + { + #include + -+ static const ${Device::Device} force_rtx_device_decl; ++ static const ${Device::AbstractDevice} force_rtx_device_decl; + } + + chunk decl() @@ -68,7 +90,7 @@ @@ -0,0 +1,9 @@ +interface Device : LKM +{ -+ provided type Device ++ provided type AbstractDevice + { + chunk LKM::includes(); + method decl(); @@ -188,7 +210,7 @@ */ template type e1000::TxRing() { -@@ -148,11 +151,88 @@ +@@ -148,11 +151,112 @@ struct rtx_e1000_tx_ring { unsigned int size; @@ -198,13 +220,14 @@ + struct rtx_e1000_tx_descriptor *base; /* rename to descs */ + dma_addr_t dma_base; + ${Socket::SKBuff} skbuffs[${config.tx_ring_size}]; -+ unsigned int head; -+ unsigned int tail; ++ unsigned short head; ++ unsigned short tail; }; } + chunk LKM::prototypes() + { ++ static void rtx_e1000_tx_ring_clean(struct rtx_e1000_tx_ring *); + static unsigned int rtx_e1000_tx_ring_descriptors_remaining(struct rtx_e1000_tx_ring *); + static int rtx_e1000_tx_ring_tso_cksum_offload(struct rtx_e1000_tx_ring *, struct rtx_socket_skbuff *); + static void rtx_e1000_tx_ring_put(struct rtx_e1000_tx_ring *, struct rtx_socket_skbuff *); @@ -213,6 +236,22 @@ + + chunk LKM::code() + { ++ static void rtx_e1000_tx_ring_clean(struct rtx_e1000_tx_ring *self) ++ { ++ ${e1000::TxDescriptor} *tx_desc; ++ bool done; ++ ++ for (; self->head != self->tail; self->head++) ++ { ++ tx_desc = &self->base[self->head]; ++ done = tx_desc->upper.fields.status & E1000_TXD_STAT_DD; ++ if (!done) ++ break ; ++ } ++ ++ pr_info("%s: tx_ring_clean: moving head to %d/%d", ${config.name}, self->head, ${config.tx_ring_size}); ++ } ++ + static unsigned int rtx_e1000_tx_ring_descriptors_remaining(struct rtx_e1000_tx_ring *self) + { + if (self->tail == self->head) /* ring is empty */ @@ -244,19 +283,26 @@ + tx_desc->lower.data = cpu_to_le32( + E1000_TXD_CMD_EOP | + E1000_TXD_CMD_IFCS | ++ E1000_TXD_CMD_RS | + skb_headlen(skb->skbuff)); + tx_desc->upper.data = 0; + tx_desc->buff_addr = cpu_to_le64(skb->dma_handle); + memcpy(&self->skbuffs[self->tail], skb, sizeof(*skb)); -+ self->tail = (self->tail + 1) % 256 /* XXX: ${config.tx_ring_size} */; ++ self->tail = (self->tail + 1) % ${config.tx_ring_size}; + } + + static void rtx_e1000_tx_ring_start_xmit(struct rtx_e1000_tx_ring *self, const struct rtx_e1000_ctx *hw_ctx) + { ++ pr_info("%s: start_xmit: moving tail to %d/%d", ${config.name}, self->tail, ${config.tx_ring_size}); + rtx_e1000_register_write32(hw_ctx, E1000_TDT, self->tail); + } + } + ++ chunk clean() ++ { ++ rtx_e1000_tx_ring_clean(${self}); ++ } ++ + chunk descriptors_remaining() + { + rtx_e1000_tx_ring_descriptors_remaining(${self}); @@ -279,7 +325,7 @@ chunk ::init() { } -@@ -334,6 +414,45 @@ +@@ -334,6 +438,45 @@ } } @@ -325,7 +371,7 @@ template sequence e1000::create_device() { chunk Ethernet::create_device(PCI::Device pdev, Ethernet::Device rtx_ether_ctx) -@@ -376,8 +495,7 @@ +@@ -376,8 +519,7 @@ udelay(10); /* Now we can load its mac address (thanks minix code) */ @@ -335,7 +381,7 @@ { rtx_e1000_register_write32(&${rtx_ether_ctx}->hw_ctx, E1000_EEPROM_READ, (i << 8) | 1); -@@ -420,6 +538,7 @@ +@@ -420,6 +562,7 @@ } } @@ -343,7 +389,7 @@ template sequence e1000::print_status(Ethernet::Device ctx) { chunk LKM::prototypes() -@@ -466,17 +585,19 @@ +@@ -466,17 +609,19 @@ * ${e1000.init(E1000_STATUS); // didn't work, so we used the next line * reg_status = E1000_STATUS; * ${e1000::register_read32(rtx_ether_ctx->hw_ctx, reg_status)}; @@ -365,7 +411,7 @@ { return ioread32(ctx->ioaddr + reg_offset); } -@@ -492,12 +613,12 @@ +@@ -492,12 +637,12 @@ { chunk LKM::prototypes() { @@ -380,7 +426,7 @@ { iowrite32(value, ctx->ioaddr + reg_offset); } -@@ -513,12 +634,12 @@ +@@ -513,12 +658,12 @@ { chunk LKM::prototypes() { @@ -395,7 +441,7 @@ { iowrite32(rtx_e1000_register_read32(ctx, reg_offset) | value, ctx->ioaddr + reg_offset); } -@@ -534,12 +655,12 @@ +@@ -534,12 +679,12 @@ { chunk LKM::prototypes() { @@ -410,7 +456,7 @@ { iowrite32(rtx_e1000_register_read32(ctx, reg_offset) & ~value, ctx->ioaddr + reg_offset); } -@@ -626,12 +747,18 @@ +@@ -626,12 +771,18 @@ } } @@ -431,7 +477,7 @@ /* * This part is documented in the Intel Gigabit Ethernet Controller * Software Developper manual. (You can find it in the doc/hardware -@@ -663,6 +790,8 @@ +@@ -663,6 +814,8 @@ * E1000_CRCERRS to E1000_TSCTFC. */ @@ -440,7 +486,7 @@ rtx_e1000_register_set32(hw_ctx, E1000_CTRL, E1000_CMD_ASDE | E1000_CMD_SLU); -@@ -676,7 +805,6 @@ +@@ -676,7 +829,6 @@ rtx_e1000_register_write32(hw_ctx, E1000_FCAL, 0); rtx_e1000_register_write32(hw_ctx, E1000_FCT, 0); rtx_e1000_register_write32(hw_ctx, E1000_FCTTV, 0); @@ -448,7 +494,7 @@ for (i = 0; i != 64; ++i) rtx_e1000_register_write32(hw_ctx, E1000_CRCERRS + i * 4, 0); -@@ -719,7 +847,6 @@ +@@ -719,7 +871,6 @@ /* 2. Initialize the MTA */ @@ -456,7 +502,7 @@ for (i = 0; i != 128; ++i) rtx_e1000_register_write32(hw_ctx, E1000_MTA + i * 4, 0); -@@ -733,7 +860,7 @@ +@@ -733,7 +884,7 @@ hw_ctx->rx_ring.base = dma_alloc_coherent( &${ctx}->pci_dev->dev, hw_ctx->rx_ring.size, @@ -465,7 +511,7 @@ GFP_KERNEL); if (!hw_ctx->rx_ring.base) { -@@ -747,41 +874,37 @@ +@@ -747,41 +898,37 @@ * Allocate the skbuffs, map them for DMA, and write their address * in the corresponding descriptor. */ @@ -518,7 +564,7 @@ rtx_e1000_register_write32(hw_ctx, E1000_RDLEN, hw_ctx->rx_ring.size); /* 6. Setup RDH/RDT */ -@@ -820,7 +943,7 @@ +@@ -820,7 +967,7 @@ hw_ctx->tx_ring.base = dma_alloc_coherent( &${ctx}->pci_dev->dev, hw_ctx->tx_ring.size, @@ -527,7 +573,7 @@ GFP_KERNEL); if (!hw_ctx->rx_ring.base) { -@@ -831,13 +954,15 @@ +@@ -831,16 +978,18 @@ ${Log::info("setup_device: tx descriptors allocated")}; /* 2. Save the emplacement and the size of the ring in TDBA/TDLEN */ @@ -544,8 +590,12 @@ + hw_ctx->tx_ring.tail = 0; /* 4. Set TCTL.PSP and enable the transmitter */ - rtx_e1000_register_set32(hw_ctx, E1000_TCTL, E1000_TCTL_PSP|E1000_TCTL_PSP); -@@ -860,15 +985,15 @@ +- rtx_e1000_register_set32(hw_ctx, E1000_TCTL, E1000_TCTL_PSP|E1000_TCTL_PSP); ++ rtx_e1000_register_set32(hw_ctx, E1000_TCTL, E1000_TCTL_PSP|E1000_TCTL_EN); + + ${Log::info("transmit registers configured and transmitter enabled")}; + +@@ -860,15 +1009,15 @@ { dma_unmap_single( &${ctx}->pci_dev->dev, @@ -564,7 +614,7 @@ err_rx_ring_alloc: return -ENOMEM; -@@ -876,12 +1001,15 @@ +@@ -876,12 +1025,15 @@ } } @@ -582,7 +632,7 @@ ${e1000::Context} *hw_ctx; hw_ctx = &${ctx}->hw_ctx; -@@ -890,18 +1018,17 @@ +@@ -890,18 +1042,17 @@ * - Unmap and free the skbuffs; * - Free the descriptors array. */ @@ -605,7 +655,7 @@ ${Log::info("free_rx_tx: rx ring free'ed")}; /* -@@ -909,7 +1036,7 @@ +@@ -909,7 +1060,7 @@ * - Free the descriptors array. */ dma_free_coherent(&${ctx}->pci_dev->dev, hw_ctx->tx_ring.size, @@ -614,7 +664,44 @@ ${Log::info("free_rx_tx: tx ring free'ed")}; } } -@@ -930,4 +1057,92 @@ +@@ -918,16 +1069,123 @@ + { + chunk ::CALL() + { +- int intr; +- +- intr = rtx_e1000_register_read32(&${ctx}->hw_ctx, E1000_ICR); +- if (intr) ++ unsigned int icr = rtx_e1000_register_read32(&${ctx}->hw_ctx, E1000_ICR); ++ pr_info("%s: interrupt received, ICR: 0x%x", ${config.name}, icr); ++ if (icr) + { +- if (intr & E1000_INTR_LSC) +- ${Log::info("cable link status changed")}; ++ if (icr & E1000_INTR_LSC) ++ { ++ ${Log::info("handle_interrupt: cable link status changed, dumping card status:")}; ++ ${e1000::print_status(ctx)}; ++ } ++ if (icr & (E1000_INTR_TXQE|E1000_INTR_TXDW)) ++ { ++ ${Log::info("handle_interrupt: TxRing: packet(s) sent")}; ++ /* ++ * XXX Do a Rathaxes call (how can I bind ++ * "&${ctx}->hw_ctx.tx_ring" to e1000::TxRing easily?) ++ */ ++ rtx_e1000_tx_ring_clean(&${ctx}->hw_ctx.tx_ring); ++ } ++ if (icr & E1000_INTR_RXT0) ++ { ++ ${Log::info("handle_interrupt: RxRing: packet(s) received")}; ++ } ++ if (icr & E1000_INTR_RXO) ++ { ++ ${Log::info("handle_interrupt: RxRing: overrun")}; ++ } + + return IRQ_HANDLED; } } } @@ -626,7 +713,7 @@ + } + } + -+ template sequence e1000::xmit(Ethernet::Device ctx, Socket::KernelSKBuff kernel_skb) ++ template sequence e1000::xmit(Ethernet::Device ctx, Socket::AbstractSKBuff kernel_skb) + { + chunk ::CALL() + { @@ -638,7 +725,7 @@ + ${Socket::SKBuff} skb; + ${e1000::Context} *hw_ctx; + ${e1000::TxRing} *tx_ring; -+ ${Device::Device} dev; ++ ${Device::AbstractDevice} dev; + + ${local.skb.init(kernel_skb)}; + hw_ctx = &${ctx}->hw_ctx; @@ -710,7 +797,7 @@ diff --git a/rathaxes/samples/e1000/e1000.rti b/rathaxes/samples/e1000/e1000.rti --- a/rathaxes/samples/e1000/e1000.rti +++ b/rathaxes/samples/e1000/e1000.rti -@@ -31,8 +31,15 @@ +@@ -31,8 +31,19 @@ provided type TxRing { chunk LKM::includes(); @@ -719,14 +806,18 @@ method decl(); method init(); + ++ /* Clean the ring (i.e: move the head closer to the tail): */ ++ method clean(); ++ /* Return the number of clean descriptors left in the ring: */ + method descriptors_remaining(); + method tso_cksum_offload(Socket::SKBuff); + method put(Socket::SKBuff); ++ /* Signal the device that new dirty descriptors are on the ring: */ + method start_xmit(e1000::Context); } /* -@@ -51,6 +58,12 @@ +@@ -51,6 +62,12 @@ method decl(); } @@ -739,7 +830,7 @@ provided sequence create_device() { provided chunk Ethernet::create_device(PCI::Device, Ethernet::Device); -@@ -109,6 +122,16 @@ +@@ -109,6 +126,16 @@ provided chunk ::CALL(); } @@ -748,7 +839,7 @@ + provided chunk ::CALL(); + } + -+ provided sequence xmit(Ethernet::Device, Socket::KernelSKBuff) ++ provided sequence xmit(Ethernet::Device, Socket::AbstractSKBuff) + { + provided chunk ::CALL(); + } @@ -774,8 +865,8 @@ + { + static const struct + { -+ const unsigned short id; -+ const char *name; ++ unsigned short id; ++ const char *name; + } rtx_ethernet_proto_table[] = + { + { ETH_P_IP, "IPv4" }, @@ -789,7 +880,7 @@ + static const char *rtx_ethernet_protocol_id_to_str(unsigned short proto_id) + { + for (int i = 0; -+ i != sizeof(rtx_ethernet_proto_table[0]) / sizeof(rtx_ethernet_proto_table); ++ i != sizeof(rtx_ethernet_proto_table) / sizeof(rtx_ethernet_proto_table[0]); + i++) + if (proto_id == rtx_ethernet_proto_table[i].id) + return rtx_ethernet_proto_table[i].name; @@ -866,7 +957,7 @@ } - template sequence Ethernet::send(Ethernet::Device dev, Socket::SKBuff skb) -+ template sequence Ethernet::send(Ethernet::Device dev, Socket::KernelSKBuff skb) ++ template sequence Ethernet::send(Ethernet::Device dev, Socket::AbstractSKBuff skb) { chunk LKM::prototypes() { @@ -883,7 +974,7 @@ - - return 0; + ${Ethernet::Device} rtx_ethernet_dev = netdev_priv(net_dev); -+ ${cast local.kernel_skb as Socket::KernelSKBuff}; ++ ${cast local.kernel_skb as Socket::AbstractSKBuff}; + ${pointcut ::IMPLEMENTATION(local.rtx_ethernet_dev, local.kernel_skb)}; } } @@ -954,7 +1045,7 @@ } - required sequence send(Ethernet::Device dev, Socket::SKBuff skb) -+ required sequence send(Ethernet::Device, Socket::KernelSKBuff) ++ required sequence send(Ethernet::Device, Socket::AbstractSKBuff) { provided chunk LKM::prototypes(); provided chunk LKM::code(); @@ -972,38 +1063,53 @@ } - Ethernet::send(Ethernet::Device dev, Socket::SKBuff skb) -+ Ethernet::send(Ethernet::Device dev, Socket::KernelSKBuff skb) ++ Ethernet::send(Ethernet::Device dev, Socket::AbstractSKBuff skb) { Log::info("we have one packet to transmit!"); + e1000::xmit(dev, skb); } LKM::init() -@@ -79,4 +80,10 @@ +@@ -66,8 +67,13 @@ + LKM::description = "Hello World Loadable Kernel Module (LKM)"; + LKM::license = "GPL"; + ++ /* ++ * See section 5.2 of the Intel manual for all the values ++ * - 0x100e is for the 82540EM-A; ++ * - 0x100f, apparently found on vmware by default, is for the 82545EM-A. ++ */ + PCI::vendor_id = 0x8086; +- PCI::product_id = 0x100e; /* e100f on vmware by default it seems */ ++ PCI::product_id = 0x100e; + PCI::set_master = true; + + Ethernet::ifname = "rtx%d"; +@@ -79,4 +85,10 @@ * 4096, 8192 and 16384 bytes: */ e1000::rx_buffer_len = 2048; + /* + * 4096 bytes maximum per transmit descriptor is used on Linux and FreeBSD, + * 2048 on Minix and HelenOS, I can't find why. If I understand the Intel -+ * man correctly, the maximum should be 16288 (see section 3.3.3). ++ * manual correctly, the maximum should be 16288 (see section 3.3.3). + */ + e1000::tx_max_data_per_desc = 4096; } diff --git a/rathaxes/samples/e1000/socket.blt b/rathaxes/samples/e1000/socket.blt --- a/rathaxes/samples/e1000/socket.blt +++ b/rathaxes/samples/e1000/socket.blt -@@ -1,20 +1,153 @@ +@@ -1,20 +1,152 @@ -with Socket, LKM +with Socket, LKM, Device, Ethernet { -+ template type Socket::KernelSKBuff() ++ template type Socket::AbstractSKBuff() + { + chunk LKM::includes() + { + #include + -+ static const ${Socket::KernelSKBuff} force_rtx_socket_kernel_skbuff_decl; ++ static const ${Socket::AbstractSKBuff} force_rtx_socket_kernel_skbuff_decl; + } + + chunk ::decl() @@ -1061,15 +1167,14 @@ + struct skb_shared_info *shinfo = skb_shinfo(self->skbuff); + + pr_info( -+ "\t protocol = %#-5x (%s)\n" ++ "\t protocol = %#-5x (%s) ip_summed = %d (%s)\n" + "\t len = %-5u data_len = %-5u head_len = %-5u\n" + "\t nr_frags = %u\n" -+ "\t gso_size = %-5u gso_segs = %-5u gso_type = %-5u\n" -+ "\tip_summed = %d (%s)", ++ "\t gso_size = %-5u gso_segs = %-5u gso_type = %-5u", + ethernet_proto, rtx_ethernet_protocol_id_to_str(ethernet_proto) /* XXX: ${local.ethernet_proto.to_str()} */, ++ self->skbuff->ip_summed, ip_summed_values[self->skbuff->ip_summed], + self->skbuff->len, self->skbuff->data_len, skb_headlen(self->skbuff), -+ shinfo->nr_frags, shinfo->gso_size, shinfo->gso_segs, shinfo->gso_type, -+ self->skbuff->ip_summed, ip_summed_values[self->skbuff->ip_summed] ++ shinfo->nr_frags, shinfo->gso_size, shinfo->gso_segs, shinfo->gso_type + ); + } + @@ -1120,7 +1225,7 @@ + * correct C variable from Ethernet::send() (so I named it as the C + * variable I needed) + */ -+ chunk ::init(Socket::KernelSKBuff kernel_skb) ++ chunk ::init(Socket::AbstractSKBuff kernel_skb) + { + ${self}.skbuff = kernel_skb; + ${self}.dma_handle = 0; @@ -1131,22 +1236,22 @@ + rtx_socket_skbuff_dump_infos(${self}); + } + -+ chunk map_to(Device::Device dev) ++ chunk map_to(Device::AbstractDevice dev) + { + rtx_socket_skbuff_map(${self}, ${dev}, DMA_TO_DEVICE); + } + -+ chunk map_from(Device::Device dev) ++ chunk map_from(Device::AbstractDevice dev) + { + rtx_socket_skbuff_map(${self}, ${dev}, DMA_FROM_DEVICE); + } + -+ chunk unmap_to_and_free(Device::Device dev) ++ chunk unmap_to_and_free(Device::AbstractDevice dev) + { + rtx_socket_skbuff_unmap_and_free(${self}, ${dev}, DMA_TO_DEVICE); + } + -+ chunk unmap_from_and_free(Device::Device dev) ++ chunk unmap_from_and_free(Device::AbstractDevice dev) + { + rtx_socket_skbuff_unmap_and_free(${self}, ${dev}, DMA_FROM_DEVICE); } @@ -1155,7 +1260,7 @@ diff --git a/rathaxes/samples/e1000/socket.rti b/rathaxes/samples/e1000/socket.rti --- a/rathaxes/samples/e1000/socket.rti +++ b/rathaxes/samples/e1000/socket.rti -@@ -1,8 +1,22 @@ +@@ -1,8 +1,23 @@ -interface Socket : LKM +interface Socket : LKM, Device { @@ -1163,7 +1268,8 @@ - chunk LKM::includes(); - method decl(); - method init(); -+ provided type KernelSKBuff ++ /* The SKBuff type from the kernel */ ++ provided type AbstractSKBuff + { + chunk LKM::includes(); + method decl(); @@ -1175,11 +1281,11 @@ + chunk LKM::prototypes(); + chunk LKM::code(); + method decl(); -+ method init(Socket::KernelSKBuff); ++ method init(Socket::AbstractSKBuff); + method dump_infos(); -+ method map_to(Device::Device); -+ method map_from(Device::Device); -+ method unmap_to_and_free(Device::Device); -+ method unmap_from_and_free(Device::Device); ++ method map_to(Device::AbstractDevice); ++ method map_from(Device::AbstractDevice); ++ method unmap_to_and_free(Device::AbstractDevice); ++ method unmap_from_and_free(Device::AbstractDevice); } }