diff --git a/src/xrt/ipc/shared/ipcproto/common.py b/src/xrt/ipc/shared/ipcproto/common.py index c55a477b3..654ef5e5d 100644 --- a/src/xrt/ipc/shared/ipcproto/common.py +++ b/src/xrt/ipc/shared/ipcproto/common.py @@ -241,6 +241,18 @@ class Call: for arg in self.out_args: arg.dump() + def write_send_decl(self, f): + """Write declaration of ipc_send_CALLNAME_locked.""" + args = ["struct ipc_connection *ipc_c"] + args.extend(arg.get_func_argument_in() for arg in self.in_args) + write_decl(f, 'xrt_result_t', 'ipc_send_' + self.name + "_locked", args) + + def write_receive_decl(self, f): + """Write declaration of ipc_receive_CALLNAME_locked.""" + args = ["struct ipc_connection *ipc_c"] + args.extend(arg.get_func_argument_out() for arg in self.out_args) + write_decl(f, 'xrt_result_t', 'ipc_receive_' + self.name + "_locked", args) + def write_call_decl(self, f): """Write declaration of ipc_call_CALLNAME.""" args = ["struct ipc_connection *ipc_c"] @@ -255,12 +267,19 @@ class Call: def write_handler_decl(self, f): """Write declaration of ipc_handle_CALLNAME.""" args = ["volatile struct ipc_client_state *ics"] + + # Always get in arguments. args.extend(arg.get_func_argument_in() for arg in self.in_args) - args.extend(arg.get_func_argument_out() for arg in self.out_args) + + # Handle sending reply in the function itself. + if not self.varlen: + args.extend(arg.get_func_argument_out() for arg in self.out_args) + if self.out_handles: args.extend(self.out_handles.handler_arg_decls) if self.in_handles: args.extend(self.in_handles.const_arg_decls) + write_decl(f, 'xrt_result_t', 'ipc_handle_' + self.name, args) @property @@ -276,6 +295,7 @@ class Call: self.out_args = [] self.in_handles = None self.out_handles = None + self.varlen = False for key, val in data.items(): if key == 'id': self.id = val @@ -287,10 +307,14 @@ class Call: self.out_handles = HandleType(val) elif key == 'in_handles': self.in_handles = HandleType(val) + elif key == 'varlen': + self.varlen = val else: raise RuntimeError("Unrecognized key") if not self.id: self.id = "IPC_" + name.upper() + if self.varlen and (self.in_handles or self.out_handles): + raise Exception("Can not have handles with varlen functions") class Proto: diff --git a/src/xrt/ipc/shared/proto.py b/src/xrt/ipc/shared/proto.py index 8189b3cc9..9bf6e9988 100755 --- a/src/xrt/ipc/shared/proto.py +++ b/src/xrt/ipc/shared/proto.py @@ -21,6 +21,116 @@ header = '''// Copyright 2020-2023, Collabora, Ltd. ''' +def write_send_definition(f, call): + """Write a ipc_send_CALLNAME_locked function.""" + call.write_send_decl(f) + f.write("\n{\n") + f.write("\tIPC_TRACE(ipc_c, \"Sending " + call.name + "\");\n\n") + + write_msg_struct(f, call, '\t') + + write_msg_send(f, 'xrt_result_t ret', indent="\t") + + f.write("\n\treturn ret;\n}\n") + + +def write_receive_definition(f, call): + """Write a ipc_receive_CALLNAME_locked function.""" + call.write_receive_decl(f) + f.write("\n{\n") + f.write("\tIPC_TRACE(ipc_c, \"Receiving " + call.name + "\");\n\n") + + write_reply_struct(f, call, '\t') + + f.write("\n\t// Await the reply") + func = 'ipc_receive' + args = ['&ipc_c->imc', '&_reply', 'sizeof(_reply)'] + write_invocation(f, 'xrt_result_t ret', func, args, indent="\t") + f.write(";") + write_result_handler(f, 'ret', None, indent="\t") + + for arg in call.out_args: + f.write("\t*out_" + arg.name + " = _reply." + arg.name + ";\n") + + f.write("\n\treturn _reply.result;\n}\n") + + +def write_call_definition(f, call): + """Write a ipc_call_CALLNAME function.""" + call.write_call_decl(f) + f.write("\n{\n") + + f.write("\tIPC_TRACE(ipc_c, \"Calling " + call.name + "\");\n\n") + + write_msg_struct(f, call, '\t') + write_reply_struct(f, call, '\t') + + f.write(""" +\t// Other threads must not read/write the fd while we wait for reply +\tos_mutex_lock(&ipc_c->mutex); +""") + cleanup = "os_mutex_unlock(&ipc_c->mutex);" + + # Prepare initial sending + write_msg_send(f, 'xrt_result_t ret', indent="\t") + write_result_handler(f, 'ret', cleanup, indent="\t") + + if call.in_handles: + f.write("\n\t// Send our handles separately\n") + f.write("\n\t// Wait for server sync") + # Must sync with the server so it's expecting the next message. + write_invocation( + f, + 'ret', + 'ipc_receive', + ( + '&ipc_c->imc', + '&_sync', + 'sizeof(_sync)' + ), + indent="\t" + ) + f.write(';') + write_result_handler(f, 'ret', cleanup, indent="\t") + + # Must send these in a second message + # since the server doesn't know how many to expect. + f.write("\n\t// We need this message data as filler only\n") + f.write("\tstruct ipc_command_msg _handle_msg = {\n") + f.write("\t .cmd = " + str(call.id) + ",\n") + f.write("\t};\n") + write_invocation( + f, + 'ret', + 'ipc_send_handles_' + call.in_handles.stem, + ( + '&ipc_c->imc', + "&_handle_msg", + "sizeof(_handle_msg)", + call.in_handles.arg_name, + call.in_handles.count_arg_name + ), + indent="\t" + ) + f.write(';') + write_result_handler(f, 'ret', cleanup, indent="\t") + + f.write("\n\t// Await the reply") + func = 'ipc_receive' + args = ['&ipc_c->imc', '&_reply', 'sizeof(_reply)'] + if call.out_handles: + func += '_handles_' + call.out_handles.stem + args.extend(call.out_handles.arg_names) + write_invocation(f, 'ret', func, args, indent="\t") + f.write(';') + write_result_handler(f, 'ret', cleanup, indent="\t") + + for arg in call.out_args: + f.write("\t*out_" + arg.name + " = _reply." + arg.name + ";\n") + f.write("\n\t" + cleanup) + f.write("\n\treturn _reply.result;\n}\n") + + def generate_h(file, p): """Generate protocol header. @@ -109,78 +219,12 @@ def generate_client_c(file, p): # Loop over all of the calls. for call in p.calls: - call.write_call_decl(f) - f.write("\n{\n") + if call.varlen: + write_send_definition(f, call) + write_receive_definition(f, call) + else: + write_call_definition(f, call) - f.write("\tIPC_TRACE(ipc_c, \"Calling " + call.name + "\");\n\n") - - write_msg_struct(f, call, '\t') - write_reply_struct(f, call, '\t') - - f.write(""" -\t// Other threads must not read/write the fd while we wait for reply -\tos_mutex_lock(&ipc_c->mutex); -""") - cleanup = "os_mutex_unlock(&ipc_c->mutex);" - - # Prepare initial sending - write_msg_send(f, 'xrt_result_t ret', indent="\t") - write_result_handler(f, 'ret', cleanup, indent="\t") - - if call.in_handles: - f.write("\n\t// Send our handles separately\n") - f.write("\n\t// Wait for server sync") - # Must sync with the server so it's expecting the next message. - write_invocation( - f, - 'ret', - 'ipc_receive', - ( - '&ipc_c->imc', - '&_sync', - 'sizeof(_sync)' - ), - indent="\t" - ) - f.write(';') - write_result_handler(f, 'ret', cleanup, indent="\t") - - # Must send these in a second message - # since the server doesn't know how many to expect. - f.write("\n\t// We need this message data as filler only\n") - f.write("\tstruct ipc_command_msg _handle_msg = {\n") - f.write("\t .cmd = " + str(call.id) + ",\n") - f.write("\t};\n") - write_invocation( - f, - 'ret', - 'ipc_send_handles_' + call.in_handles.stem, - ( - '&ipc_c->imc', - "&_handle_msg", - "sizeof(_handle_msg)", - call.in_handles.arg_name, - call.in_handles.count_arg_name - ), - indent="\t" - ) - f.write(';') - write_result_handler(f, 'ret', cleanup, indent="\t") - - f.write("\n\t// Await the reply") - func = 'ipc_receive' - args = ['&ipc_c->imc', '&_reply', 'sizeof(_reply)'] - if call.out_handles: - func += '_handles_' + call.out_handles.stem - args.extend(call.out_handles.arg_names) - write_invocation(f, 'ret', func, args, indent="\t") - f.write(';') - write_result_handler(f, 'ret', cleanup, indent="\t") - - for arg in call.out_args: - f.write("\t*out_" + arg.name + " = _reply." + arg.name + ";\n") - f.write("\n\t" + cleanup) - f.write("\n\treturn _reply.result;\n}\n") f.close() @@ -203,7 +247,12 @@ def generate_client_h(file, p): f.write("\n") for call in p.calls: - call.write_call_decl(f) + if call.varlen: + call.write_send_decl(f) + f.write(";\n") + call.write_receive_decl(f) + else: + call.write_call_decl(f) f.write(";\n") write_cpp_header_guard_end(f) @@ -244,10 +293,14 @@ ipc_dispatch(volatile struct ipc_client_state *ics, ipc_command_t *ipc_command) f.write( "\t\tstruct ipc_{}_msg *msg = ".format(call.name)) f.write("(struct ipc_{}_msg *)ipc_command;\n".format(call.name)) - if call.out_args: + + if call.varlen: + f.write("\t\t// No return arguments") + elif call.out_args: f.write("\t\tstruct ipc_%s_reply reply = {0};\n" % call.name) else: f.write("\t\tstruct ipc_result_reply reply = {0};\n") + if call.in_handles: # We need to fetch these handles separately f.write("\t\tstruct ipc_result_reply _sync = {XRT_SUCCESS};\n") @@ -305,11 +358,17 @@ ipc_dispatch(volatile struct ipc_client_state *ics, ipc_command_t *ipc_command) # Write call to ipc_handle_CALLNAME args = ["ics"] + + # Always provide in arguments. for arg in call.in_args: args.append(("&msg->" + arg.name) if arg.is_aggregate else ("msg->" + arg.name)) - args.extend("&reply." + arg.name for arg in call.out_args) + + # No reply arguments on varlen. + if not call.varlen: + args.extend("&reply." + arg.name for arg in call.out_args) + if call.out_handles: args.extend(("XRT_MAX_IPC_HANDLES", call.out_handles.arg_name, @@ -318,23 +377,31 @@ ipc_dispatch(volatile struct ipc_client_state *ics, ipc_command_t *ipc_command) if call.in_handles: args.extend(("&in_%s[0]" % call.in_handles.arg_name, "msg->"+call.in_handles.count_arg_name)) - write_invocation(f, 'reply.result', 'ipc_handle_' + + + # Should we put the return in the reply or return it? + return_target = 'reply.result' + if call.varlen: + return_target = 'xrt_result_t xret' + + write_invocation(f, return_target, 'ipc_handle_' + call.name, args, indent="\t\t") f.write(";\n") # TODO do we check reply.result and # error out before replying if it's not success? - func = 'ipc_send' - args = ["(struct ipc_message_channel *)&ics->imc", - "&reply", - "sizeof(reply)"] - if call.out_handles: - func += '_handles_' + call.out_handles.stem - args.extend(call.out_handles.arg_names) - write_invocation(f, 'xrt_result_t ret', func, args, indent="\t\t") - f.write(";") - f.write("\n\t\treturn ret;\n") + if not call.varlen: + func = 'ipc_send' + args = ["(struct ipc_message_channel *)&ics->imc", + "&reply", + "sizeof(reply)"] + if call.out_handles: + func += '_handles_' + call.out_handles.stem + args.extend(call.out_handles.arg_names) + write_invocation(f, 'xrt_result_t xret', func, args, indent="\t\t") + f.write(";") + + f.write("\n\t\treturn xret;\n") f.write("\t}\n") f.write('''\tdefault: \t\tU_LOG_E("UNHANDLED IPC MESSAGE! %d", *ipc_command);