From: x-fn-spp@sl.ntt-tx.co.jp
To: spp@dpdk.org
Subject: [spp] [PATCH 47/57] spp_vf: add spp_vf.py instead of spp.py
Date: Thu, 28 Dec 2017 13:55:54 +0900 [thread overview]
Message-ID: <201712280456.vBS4u8Gp011168@imss03.silk.ntt-tx.co.jp> (raw)
In-Reply-To: <4aae78ff-3b6c-cdfe-a8b7-24ec08b73935@lab.ntt.co.jp>
From: Hiroyuki Nakamura <nakamura.hioryuki@po.ntt-tx.co.jp>
Add spp_vf.py as a modified version of spp.py.
Signed-off-by: Kentaro Watanabe <watanabe.kentaro.z01@as.ntt-tx.co.jp>
Signed-off-by: Yasufum Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
---
src/spp_vf.py | 443 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 443 insertions(+)
create mode 100755 src/spp_vf.py
diff --git a/src/spp_vf.py b/src/spp_vf.py
new file mode 100755
index 0000000..40310db
--- /dev/null
+++ b/src/spp_vf.py
@@ -0,0 +1,443 @@
+#!/usr/bin/python
+"""Soft Patch Panel"""
+
+from __future__ import print_function
+from Queue import Queue
+from thread import start_new_thread
+from threading import Thread
+import cmd
+import getopt
+import select
+import socket
+import sys
+
+import json
+
+class GrowingList(list):
+ """GrowingList"""
+
+ def __setitem__(self, index, value):
+ if index >= len(self):
+ self.extend([None]*(index + 1 - len(self)))
+ list.__setitem__(self, index, value)
+
+MAX_SECONDARY = 16
+
+# init
+PRIMARY = ''
+SECONDARY_LIST = []
+SECONDARY_COUNT = 0
+
+#init primary comm channel
+MAIN2PRIMARY = Queue()
+PRIMARY2MAIN = Queue()
+
+#init secondary comm channel list
+MAIN2SEC = GrowingList()
+SEC2MAIN = GrowingList()
+
+def connectionthread(name, client_id, conn, m2s, s2m):
+ """Manage secondary process connections"""
+
+ cmd_str = 'hello'
+
+ #infinite loop so that function do not terminate and thread do not end.
+ while True:
+ try:
+ _, _, _ = select.select([conn,], [conn,], [], 5)
+ except select.error:
+ break
+
+ #Sending message to connected secondary
+ try:
+ cmd_str = m2s.get(True)
+ conn.send(cmd_str) #send only takes string
+ except KeyError:
+ break
+ except Exception, excep:
+ print (str(excep))
+ break
+
+ #Receiving from secondary
+ try:
+ data = conn.recv(1024) # 1024 stands for bytes of data to be received
+ if data:
+ s2m.put("recv:" + str(conn.fileno()) + ":" + "{" + data + "}")
+ else:
+ s2m.put("closing:" + str(conn))
+ break
+ except Exception, excep:
+ print (str(excep))
+ break
+
+ SECONDARY_LIST.remove(client_id)
+ conn.close()
+
+def getclientid(conn):
+ """Get client_id from client"""
+
+ try:
+ conn.send("_get_client_id")
+ #conn.send("{\"commands\":[{\"command\":\"process\"}]}")
+ except KeyError:
+ return -1
+
+ data = conn.recv(1024)
+ if data == None:
+ return -1
+
+ #client_id = int(data.strip('\0'))
+ json_dict = json.loads(data)
+ client_id = int(json_dict['client_id'])
+
+ if client_id < 0 or client_id > MAX_SECONDARY:
+ return -1
+
+ print ("secondary id %d" % client_id)
+ return client_id
+
+ found = 0
+ for i in SECONDARY_LIST:
+ if client_id == i:
+ found = 1
+ break
+
+ if found == 0:
+ return client_id
+
+ # client_id in use, find a free one
+ free_client_id = -1
+ for i in range(MAX_SECONDARY):
+ found = -1
+ for j in SECONDARY_LIST:
+ if i == j:
+ found = i
+ break
+ if found == -1:
+ free_client_id = i
+ break
+
+ if free_client_id < 0:
+ return -1
+
+ conn.send("_set_client_id %u" % free_client_id)
+ data = conn.recv(1024)
+
+ return free_client_id
+
+def acceptthread(sock, main2sec, sec2main):
+ """Listen for secondary processes"""
+
+ global SECONDARY_COUNT
+
+ try:
+ while True:
+ #Accepting incoming connections
+ conn, _ = sock.accept()
+
+ client_id = getclientid(conn)
+ if client_id < 0:
+ break
+
+ #Creating new thread.
+ #Calling secondarythread function for this function and passing
+ #conn as argument.
+
+ SECONDARY_LIST.append(client_id)
+ main2sec[client_id] = Queue()
+ sec2main[client_id] = Queue()
+ start_new_thread(connectionthread,
+ ('secondary', client_id, conn,
+ main2sec[client_id],
+ sec2main[client_id], ))
+ SECONDARY_COUNT += 1
+ except Exception, excep:
+ print (str(excep))
+ sock.close()
+
+def command_primary(command):
+ """Send command to primary process"""
+
+ if PRIMARY:
+ MAIN2PRIMARY.put(command)
+ print (PRIMARY2MAIN.get(True))
+ else:
+ print ("primary not started")
+
+def command_secondary(sec_id, command):
+ """Send command to secondary process with sec_id"""
+
+ if sec_id in SECONDARY_LIST:
+ MAIN2SEC[sec_id].put(command)
+ print (SEC2MAIN[sec_id].get(True))
+ else:
+ print ("secondary id %d not exist" % sec_id)
+
+def print_status():
+ """Display information about connected clients"""
+
+ print ("Soft Patch Panel Status :")
+ print ("primary: %d" % PRIMARY)
+ print ("secondary count: %d" % len(SECONDARY_LIST))
+ for i in SECONDARY_LIST:
+ print ("Connected secondary id: %d" % i)
+
+def primarythread(sock, main2primary, primary2main):
+ """Manage primary process connection"""
+
+ global PRIMARY
+ cmd_str = ''
+
+ while True:
+ #waiting for connection
+ PRIMARY = False
+ conn, addr = sock.accept()
+ PRIMARY = True
+
+ while conn:
+ try:
+ _, _, _ = select.select([conn,], [conn,], [], 5)
+ except select.error:
+ break
+
+ #Sending message to connected primary
+ try:
+ cmd_str = main2primary.get(True)
+ conn.send(cmd_str) #send only takes string
+ except KeyError:
+ break
+ except Exception, excep:
+ print (str(excep))
+ break
+
+ #Receiving from primary
+ try:
+ data = conn.recv(1024) # 1024 stands for bytes of data to be received
+ if data:
+ primary2main.put("recv:" + str(addr) + ":" + "{" + data + "}")
+ else:
+ primary2main.put("closing:" + str(addr))
+ conn.close()
+ break
+ except Exception, excep:
+ print (str(excep))
+ break
+
+ print ("primary communication thread end")
+
+def close_all_secondary():
+ """Exit all secondary processes"""
+
+ return;
+
+ global SECONDARY_COUNT
+
+ tmp_list = []
+ for i in SECONDARY_LIST:
+ tmp_list.append(i)
+ for i in tmp_list:
+ command_secondary(i, 'exit')
+ SECONDARY_COUNT = 0
+
+def check_sec_cmds(cmds):
+ """Validate secondary commands before sending"""
+
+ return 1
+
+ level1 = ['status', 'exit', 'forward', 'stop']
+ level2 = ['add', 'patch', 'del']
+ patch_args = ['reset']
+ add_del_args = ['ring', 'vhost']
+ cmdlist = cmds.split(' ')
+ valid = 0
+
+ length = len(cmdlist)
+ if length == 1:
+ if cmdlist[0] in level1:
+ valid = 1
+ elif length == 2:
+ if cmdlist[0] == 'patch':
+ if cmdlist[1] in patch_args:
+ valid = 1
+ elif length == 3:
+ if cmdlist[0] in level2:
+ if cmdlist[0] == 'add' or cmdlist[0] == 'del':
+ if cmdlist[1] in add_del_args:
+ if str.isdigit(cmdlist[2]):
+ valid = 1
+ elif cmdlist[0] == 'patch':
+ if str.isdigit(cmdlist[1]) and str.isdigit(cmdlist[2]):
+ valid = 1
+
+ return valid
+
+class Shell(cmd.Cmd):
+ """SPP command prompt"""
+
+ intro = 'Welcome to the spp. Type help or ? to list commands.\n'
+ prompt = 'spp > '
+ recorded_file = None
+
+ COMMANDS = ['status', 'add', 'patch', 'ring', 'vhost',
+ 'reset', 'exit', 'forward', 'stop', 'clear']
+
+ def complete_pri(self, text, line, begidx, endidx):
+ """Completion for primary process commands"""
+
+ if not text:
+ completions = self.COMMANDS[:]
+ else:
+ completions = [p
+ for p in self.COMMANDS
+ if p.startswith(text)
+ ]
+ return completions
+
+ def complete_sec(self, text, line, begidx, endidx):
+ """Completion for secondary process commands"""
+
+ if not text:
+ completions = self.COMMANDS[:]
+ else:
+ completions = [p
+ for p in self.COMMANDS
+ if p.startswith(text)
+ ]
+ return completions
+
+ def do_status(self, _):
+ """Display Soft Patch Panel Status"""
+
+ print_status()
+
+ def do_pri(self, command):
+ """Send command to primary process"""
+
+ if command and command in self.COMMANDS:
+ command_primary(command)
+ else:
+ print ("primary invalid command")
+
+ def do_sec(self, arg):
+ """Send command to secondary process"""
+
+ cmds = arg.split(';')
+ if len(cmds) < 2:
+ print ("error")
+ elif str.isdigit(cmds[0]):
+ sec_id = int(cmds[0])
+ if check_sec_cmds(cmds[1]):
+ command_secondary(sec_id, cmds[1])
+ else:
+ print ("invalid cmd")
+ else:
+ print (cmds[0])
+ print ("first %s" % cmds[1])
+
+ def do_record(self, arg):
+ """Save future commands to filename: RECORD filename.cmd"""
+
+ self.recorded_file = open(arg, 'w')
+
+ def do_playback(self, arg):
+ """Playback commands from a file: PLAYBACK filename.cmd"""
+
+ self.close()
+ try:
+ with open(arg) as recorded_file:
+ lines = []
+ for line in recorded_file:
+ if line.strip().startswith("#"):
+ continue
+ lines.append(line)
+ self.cmdqueue.extend(lines)
+ except IOError:
+ print ("Error: File does not exist.")
+
+ def precmd(self, line):
+ line = line.lower()
+ if self.recorded_file and 'playback' not in line:
+ print(line, file=self.recorded_file)
+ return line
+
+ def close(self):
+ """Close record file"""
+
+ if self.recorded_file:
+ print("closing file")
+ self.recorded_file.close()
+ self.recorded_file = None
+
+ def do_bye(self, arg):
+ """Stop recording, close SPP, and exit: BYE"""
+
+ cmds = arg.split(' ')
+ if cmds[0] == 'sec':
+ close_all_secondary()
+ elif cmds[0] == 'all':
+ close_all_secondary()
+ command_primary('exit')
+ elif cmds[0] == '':
+ print('Thank you for using Soft Patch Panel')
+ self.close()
+ return True
+
+def main(argv):
+ """main"""
+
+ # Defining server address and port
+ host = '' #'localhost' or '127.0.0.1' or '' are all same
+
+ try:
+ opts, _ = getopt.getopt(argv, "p:s:h", ["help", "primary = ", "secondary"])
+ except getopt.GetoptError:
+ print ('spp.py -p <primary__port_number> -s <secondary_port_number>')
+ sys.exit(2)
+ for opt, arg in opts:
+ if opt in ("-h", "--help"):
+ print ('spp.py -p <primary__port_number> -s <secondary_port_number>')
+ sys.exit()
+ elif opt in ("-p", "--primary"):
+ primary_port = int(arg)
+ print ("primary port : %d" % primary_port)
+ elif opt in ("-s", "--secondary"):
+ secondary_port = int(arg)
+ print ('secondary port : %d' % secondary_port)
+
+ #Creating primary socket object
+ primary_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+ #Binding primary socket to a address. bind() takes tuple of host and port.
+ primary_sock.bind((host, primary_port))
+
+ #Listening primary at the address
+ primary_sock.listen(1) #5 denotes the number of clients can queue
+
+ primary_thread = Thread(target=primarythread,
+ args=(primary_sock, MAIN2PRIMARY, PRIMARY2MAIN,))
+ primary_thread.daemon = True
+ primary_thread.start()
+
+ #Creating secondary socket object
+ secondary_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+ #Binding secondary socket to a address. bind() takes tuple of host and port.
+ secondary_sock.bind((host, secondary_port))
+
+ #Listening secondary at the address
+ secondary_sock.listen(MAX_SECONDARY)
+
+ # secondary process handling thread
+ start_new_thread(acceptthread, (secondary_sock, MAIN2SEC, SEC2MAIN))
+
+ shell = Shell()
+ shell.cmdloop()
+ shell = None
+
+ primary_sock.shutdown(socket.SHUT_RDWR)
+ primary_sock.close()
+ secondary_sock.shutdown(socket.SHUT_RDWR)
+ secondary_sock.close()
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
--
1.9.1
next prev parent reply other threads:[~2017-12-28 4:56 UTC|newest]
Thread overview: 97+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-12-25 4:41 [spp] Proposal - spp_vf(SR-IOV like feature) addition to SPP Nakamura Hioryuki
2017-12-26 1:54 ` Yasufumi Ogawa
2017-12-28 4:55 ` [spp] [PATCH 01/57] spp_vf: add vf functions x-fn-spp
2017-12-28 8:49 ` Yasufumi Ogawa
2017-12-28 4:55 ` [spp] [PATCH 02/57] spp_vf: support multi process x-fn-spp
2018-02-07 16:50 ` Ferruh Yigit
2018-02-08 1:21 ` Yasufumi Ogawa
2018-02-08 6:44 ` [spp] [spp 02168] " Nakamura Hioryuki
2017-12-28 4:55 ` [spp] [PATCH 03/57] spp_vf: comment out check of using cores x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 04/57] spp_vf: modify classifier for upd command x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 05/57] spp_vf: add procedure that mac address is null x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 06/57] spp_vf: change config format for upd command x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 07/57] spp_vf: fix compiler warning in classifier_mac.c x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 08/57] spp_vf: modify data struct for upd command x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 09/57] spp_vf: add functions of command acceptance x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 10/57] spp_vf: add command acceptance calling x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 11/57] spp_vf: fix compiler warning in command_proc.c x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 12/57] " x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 13/57] spp_vf: refactor command acceptance/decode x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 14/57] spp_vf: fix return value overwrite problem x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 15/57] spp_vf: initialize message buffer x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 16/57] spp_vf: add const keyword to parameter x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 17/57] spp_vf: fix wrong comparison operator x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 18/57] spp_vf: fix wrong variable to be assigned x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 19/57] spp_vf: refactor parsing server ip address x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 20/57] spp_vf: fix error in command decode x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 21/57] spp_vf: fix comparison operator in spp_vf.c x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 22/57] spp_vf: check upper limit to the number of process x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 23/57] spp_vf: display usage message x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 24/57] spp_vf: split command processing source file x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 25/57] spp_vf: add new log and line break x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 26/57] spp_vf: support get-process-id command x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 27/57] spp_vf: update socket creation procedure x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 28/57] spp_vf: change log level and add line break x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 29/57] spp_vf: replace unsupported jansson api x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 30/57] spp_vf: change order of command result in json object x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 31/57] spp_vf: use prediction keywords x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 32/57] spp_vf: fix wrong comparison x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 33/57] spp_vf: update sending error status x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 34/57] spp_vf: modify conditional statement x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 35/57] spp_vf: add proc on receiving l2 multicast x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 36/57] spp_vf: extend limit on number of usable cores x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 37/57] spp_vf: add restart procedure for vhost client x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 38/57] spp_vf: fix classifier mbuf handling x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 39/57] spp_vf: add status command x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 40/57] spp_vf: add output source information in error log x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 41/57] spp_vf: change function names x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 42/57] spp_vf: change how to request commands x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 43/57] spp_vf: update command decode procedure x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 44/57] spp_vf: remove debug log output procedures x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 45/57] spp_vf: improve command_decoder program code x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 46/57] spp_vf: fix a bug in status command x-fn-spp
2017-12-28 4:55 ` x-fn-spp [this message]
2017-12-28 4:55 ` [spp] [PATCH 48/57] spp_vf: refactor for commnets in spp_vf.c x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 49/57] spp_vf: refactor comments in classifier_mac.c x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 50/57] spp_vf: refactor comments in spp_forward.c x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 51/57] spp_vf: refactor for commnets in spp_config.c x-fn-spp
2017-12-28 4:55 ` [spp] [PATCH 52/57] spp_vf: refactor no self-explanatory comments x-fn-spp
2017-12-28 4:56 ` [spp] [PATCH 53/57] spp_vf: correct typo of function name x-fn-spp
2017-12-28 4:56 ` [spp] [PATCH 54/57] spp_vf: support new command x-fn-spp
2017-12-28 4:56 ` [spp] [PATCH 55/57] spp_vf: add display of status command x-fn-spp
2017-12-28 4:56 ` [spp] [PATCH 56/57] spp_vf: fix " x-fn-spp
2017-12-28 4:56 ` [spp] [PATCH 57/57] spp_vf: fix l2 multicast packet forwarding x-fn-spp
2018-01-15 11:04 ` [spp] Proposal - spp_vf(SR-IOV like feature) addition to SPP Ferruh Yigit
2018-01-16 5:16 ` [spp] [PATCH 01/30] doc: add setup guide x-fn-spp
2018-01-19 0:52 ` Yasufumi Ogawa
2018-01-22 14:37 ` Ferruh Yigit
2018-01-23 0:25 ` Yasufumi Ogawa
2018-01-16 5:16 ` [spp] [PATCH 02/30] doc: add how_to_use.md x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 03/30] doc: add config_manual.md x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 04/30] doc: add spp_vf.md x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 05/30] doc: revise spp_vf.md x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 06/30] doc: add config section x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 07/30] doc: update jp setup manual x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 08/30] doc: modify figure in spp_vf_overview x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 09/30] doc: fix " x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 10/30] doc: fix figure in overview x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 11/30] doc: add sample usage x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 12/30] doc: revice path descs x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 13/30] doc: add network configuration diagram x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 14/30] doc: update user account x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 15/30] doc: update descriptions for todo x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 16/30] doc: add description for explanation section x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 17/30] doc: add spp_sample_usage_svg in docs/spp_vf/ x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 18/30] doc: add explanation for terminating spp app x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 19/30] doc: add explanations on options of spp x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 20/30] doc: update description for the latest spp version x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 21/30] doc: update description on dpdk version x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 22/30] doc: fix vm setup procedure for network config x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 23/30] doc: update jansson installation procedure x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 24/30] doc: fix required network configuration x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 25/30] doc: add how to use vhost-user support x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 26/30] doc: add references to hugepages and isolcpus x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 27/30] doc: remove description on classifier_table x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 28/30] doc: fix typos and erros in texts x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 29/30] doc: update sample config section x-fn-spp
2018-01-16 5:16 ` [spp] [PATCH 30/30] doc: align figure title position x-fn-spp
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=201712280456.vBS4u8Gp011168@imss03.silk.ntt-tx.co.jp \
--to=x-fn-spp@sl.ntt-tx.co.jp \
--cc=spp@dpdk.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).