@@ -208,7 +208,7 @@ func (s *StdNetBind) putMessages(msgs *[]ipv6.Message) {
208208 // Non coalesced write paths access only batch.msgs[i].Buffers[0],
209209 // but we append more during [coalesceMessages].
210210 // Leave index zero accessible:
211- (* msgs )[i ] = ipv6.Message {Buffers : (* msgs )[i ].Buffers [:1 ], OOB : (* msgs )[i ].OOB }
211+ (* msgs )[i ] = ipv6.Message {Buffers : (* msgs )[i ].Buffers [:1 ], OOB : (* msgs )[i ].OOB [: cap (( * msgs )[ i ]. OOB )] }
212212 }
213213 s .msgsPool .Put (msgs )
214214}
@@ -236,53 +236,92 @@ func (s *StdNetBind) receiveIP(
236236 rxOffload bool ,
237237 bufs []iobuf.View ,
238238 eps []Endpoint ,
239- ) (n int , err error ) {
239+ ) (int , error ) {
240240 msgs := s .getMessages ()
241- // TODO: placeholder until bind implements right-sized buffers.
242- iobuf .EnsureAllocated (bufs )
243- for i := range bufs {
244- (* msgs )[i ].Buffers [0 ] = bufs [i ].Bytes
245- (* msgs )[i ].OOB = (* msgs )[i ].OOB [:cap ((* msgs )[i ].OOB )]
246- }
247241 defer s .putMessages (msgs )
248- var numMsgs int
249- if runtime .GOOS == "linux" {
250- if rxOffload {
251- readAt := len (* msgs ) - 2
252- numMsgs , err = br .ReadBatch ((* msgs )[readAt :], 0 )
253- if err != nil {
254- return 0 , err
242+ readData := make ([]* iobuf.Shared , len (* msgs )) // TODO: optimize this allocation
243+ defer func () {
244+ for i := range readData {
245+ if readData [i ] != nil {
246+ // release backing arrays for any unclaimed buffers
247+ readData [i ].Release ()
248+ readData [i ] = nil
249+ }
250+ }
251+ }()
252+
253+ switch {
254+ case runtime .GOOS == "linux" && rxOffload :
255+ const readBatchSize = 2
256+ for i := range readBatchSize {
257+ readData [i ] = iobuf .SharedBufPool .Get ()
258+ (* msgs )[i ].Buffers [0 ] = readData [i ].Bytes [:]
259+ }
260+ msgsN , err := br .ReadBatch ((* msgs )[:readBatchSize ], 0 )
261+ if err != nil {
262+ return 0 , err // expect atomic reads
263+ }
264+ var n int
265+ for i , msg := range (* msgs )[:msgsN ] {
266+ if msg .N == 0 {
267+ continue
255268 }
256- numMsgs , err = splitCoalescedMessages ( * msgs , readAt , getGSOSize )
269+ gsoSize , err := getGSOSize ( msg . OOB [: msg . NN ] )
257270 if err != nil {
258- return 0 , err
271+ return n , err
259272 }
260- } else {
261- numMsgs , err = br .ReadBatch (* msgs , 0 )
273+ if gsoSize == 0 {
274+ gsoSize = msg .N
275+ }
276+ split , err := readData [i ].SplitCoalesced (bufs [n :], gsoSize , msg .N )
262277 if err != nil {
263- return 0 , err
278+ return n , err
279+ }
280+ addrPort := msg .Addr .(* net.UDPAddr ).AddrPort ()
281+ ep := & StdNetEndpoint {AddrPort : addrPort } // TODO: remove allocation
282+ getSrcFromControl (msg .OOB [:msg .NN ], ep )
283+ for j := n ; j < n + split ; j ++ {
284+ eps [j ] = ep
264285 }
286+ n += split
265287 }
266- } else {
288+ return n , nil
289+ case runtime .GOOS == "linux" :
290+ readBatchSize := min (len (bufs ), len (* msgs ))
291+ for i := range readBatchSize {
292+ readData [i ] = iobuf .SharedBufPool .Get ()
293+ (* msgs )[i ].Buffers [0 ] = readData [i ].Bytes [:]
294+ }
295+ msgsN , err := br .ReadBatch ((* msgs )[:readBatchSize ], 0 )
296+ if err != nil {
297+ return 0 , err // expect atomic reads
298+ }
299+ var n int
300+ for i , msg := range (* msgs )[:msgsN ] {
301+ if msg .N == 0 {
302+ continue
303+ }
304+ readData [i ].Refer (& bufs [n ], 0 , msg .N )
305+ addrPort := msg .Addr .(* net.UDPAddr ).AddrPort ()
306+ ep := & StdNetEndpoint {AddrPort : addrPort } // TODO: remove allocation
307+ getSrcFromControl (msg .OOB [:msg .NN ], ep )
308+ eps [n ] = ep
309+ n ++
310+ }
311+ return n , nil
312+ default :
267313 msg := & (* msgs )[0 ]
268- msg .N , msg .NN , _ , msg .Addr , err = conn .ReadMsgUDP (msg .Buffers [0 ], msg .OOB )
314+ readData [0 ] = iobuf .SharedBufPool .Get ()
315+ n , _ , _ , addr , err := conn .ReadMsgUDP (readData [0 ].Bytes [:], msg .OOB )
269316 if err != nil {
270317 return 0 , err
271318 }
272- numMsgs = 1
273- }
274- for i := 0 ; i < numMsgs ; i ++ {
275- msg := & (* msgs )[i ]
276- bufs [i ].Bytes = bufs [i ].Bytes [:msg .N ]
277- if len (bufs [i ].Bytes ) == 0 {
278- continue
279- }
280- addrPort := msg .Addr .(* net.UDPAddr ).AddrPort ()
281- ep := & StdNetEndpoint {AddrPort : addrPort } // TODO: remove allocation
319+ readData [0 ].Refer (& bufs [0 ], 0 , n )
320+ ep := & StdNetEndpoint {AddrPort : addr .AddrPort ()}
282321 getSrcFromControl (msg .OOB [:msg .NN ], ep )
283- eps [i ] = ep
322+ eps [0 ] = ep
323+ return 1 , nil
284324 }
285- return numMsgs , nil
286325}
287326
288327func (s * StdNetBind ) makeReceiveIPv4 (pc * ipv4.PacketConn , conn * net.UDPConn , rxOffload bool ) ReceiveFunc {
@@ -523,49 +562,3 @@ func coalesceMessages(addr *net.UDPAddr, ep *StdNetEndpoint, bufs [][]byte, offs
523562 }
524563 return base + 1
525564}
526-
527- type getGSOFunc func (control []byte ) (int , error )
528-
529- func splitCoalescedMessages (msgs []ipv6.Message , firstMsgAt int , getGSO getGSOFunc ) (n int , err error ) {
530- for i := firstMsgAt ; i < len (msgs ); i ++ {
531- msg := & msgs [i ]
532- if msg .N == 0 {
533- return n , err
534- }
535- var (
536- gsoSize int
537- start int
538- end = msg .N
539- numToSplit = 1
540- )
541- gsoSize , err = getGSO (msg .OOB [:msg .NN ])
542- if err != nil {
543- return n , err
544- }
545- if gsoSize > 0 {
546- numToSplit = (msg .N + gsoSize - 1 ) / gsoSize
547- end = gsoSize
548- }
549- for j := 0 ; j < numToSplit ; j ++ {
550- if n > i {
551- return n , errors .New ("splitting coalesced packet resulted in overflow" )
552- }
553- copied := copy (msgs [n ].Buffers [0 ], msg .Buffers [0 ][start :end ])
554- msgs [n ].N = copied
555- msgs [n ].Addr = msg .Addr
556- start = end
557- end += gsoSize
558- if end > msg .N {
559- end = msg .N
560- }
561- n ++
562- }
563- if i != n - 1 {
564- // It is legal for bytes to move within msg.Buffers[0] as a result
565- // of splitting, so we only zero the source msg len when it is not
566- // the destination of the last split operation above.
567- msg .N = 0
568- }
569- }
570- return n , nil
571- }
0 commit comments