From 195e1f19b06095f39a4fb0da46dfab2ec5b10e9a Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Thu, 13 Jan 2011 12:36:14 +0100 Subject: Implement file:sendfile Allow Erlang code to use sendfile() where available by wrapping it as file:sendfile/4 and file:sendfile/2. sendfile(2) - Linux man page: "sendfile() copies data between one file descriptor and another. Because this copying is done within the kernel, sendfile() is more efficient than the combination of read(2) and write(2), which would require transferring data to and from user space." --- erts/preloaded/src/prim_file.erl | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 30b7a5246a..f3f977a30b 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -26,7 +26,8 @@ %% Generic file contents operations -export([open/2, close/1, datasync/1, sync/1, advise/4, position/2, truncate/1, - write/2, pwrite/2, pwrite/3, read/2, read_line/1, pread/2, pread/3, copy/3]). + write/2, pwrite/2, pwrite/3, read/2, read_line/1, pread/2, pread/3, + copy/3, sendfile/5]). %% Specialized file operations -export([open/1, open/3]). @@ -98,6 +99,7 @@ -define(FILE_READ_LINE, 29). -define(FILE_FDATASYNC, 30). -define(FILE_ADVISE, 31). +-define(FILE_SENDFILE, 32). %% Driver responses -define(FILE_RESP_OK, 0). @@ -539,6 +541,21 @@ write_file(_, _) -> {error, badarg}. +%% Returns {error, Reason} | {ok, BytesCopied} +sendfile(#file_descriptor{module = ?MODULE, data = {Port, _}}, + DestFD, Offset, Bytes, ChunkSize) -> + ok = drv_command(Port, <>), + Self = self(), + %% Should we use a ref()? + receive + {efile_reply, Self, Port, {ok, _Written}=OKRes}-> + OKRes; + {efile_reply, Self, Port, {error, _PosixError}=Error}-> + Error; + Unexpected -> + Unexpected + end. %%%----------------------------------------------------------------- %%% Functions operating on files without handle to the file. ?DRV. -- cgit v1.2.3 From 5ba916ef7ac71bd1e7e23b4c87ae6a472f14fd6c Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 25 Nov 2011 12:03:58 +0100 Subject: Create erlang fallback for sendfile Created erlang fallback for sendfile in gen_tcp and moved sendfile from file to gen_tcp. Also created testcases for testing all different options to sendfile. For info about how sendfile should work see the BSD man pages as they contain a more complete API than other *nixes. --- erts/preloaded/src/prim_file.erl | 40 ++++++++++++++++++++++++++++++++++------ erts/preloaded/src/prim_inet.erl | 25 ++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 7 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index f3f977a30b..606d7d5aab 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -27,7 +27,7 @@ %% Generic file contents operations -export([open/2, close/1, datasync/1, sync/1, advise/4, position/2, truncate/1, write/2, pwrite/2, pwrite/3, read/2, read_line/1, pread/2, pread/3, - copy/3, sendfile/5]). + copy/3, sendfile/10]). %% Specialized file operations -export([open/1, open/3]). @@ -542,21 +542,49 @@ write_file(_, _) -> %% Returns {error, Reason} | {ok, BytesCopied} +sendfile(_,_,_,_,_,_,_,_,_,_) -> + {error, enotsup}; sendfile(#file_descriptor{module = ?MODULE, data = {Port, _}}, - DestFD, Offset, Bytes, ChunkSize) -> + DestFD, Offset, Bytes, ChunkSize, Headers, Trailers, + Nodiskio, MNowait, Sync) -> + ok = drv_command(Port, <>), + ChunkSize:64, + (get_bit(Nodiskio)):1, + (get_bit(MNowait)):1, + (get_bit(Sync)):1,0:5, + (encode_hdtl(Headers))/binary, + (encode_hdtl(Trailers))/binary>>), Self = self(), %% Should we use a ref()? receive {efile_reply, Self, Port, {ok, _Written}=OKRes}-> OKRes; {efile_reply, Self, Port, {error, _PosixError}=Error}-> - Error; - Unexpected -> - Unexpected + Error end. +get_bit(true) -> + 1; +get_bit(false) -> + 0. + +encode_hdtl(undefined) -> + <<0>>; +encode_hdtl([]) -> + <<0>>; +encode_hdtl(List) -> + encode_hdtl(List,<<>>,0). + +encode_hdtl([], Acc, Cnt) -> + <>; +encode_hdtl([Bin|T], Acc, Cnt) -> + encode_hdtl(T, <<(byte_size(Bin)):32, Bin/binary, Acc/binary>>,Cnt + 1). + + + + + %%%----------------------------------------------------------------- %%% Functions operating on files without handle to the file. ?DRV. %%% diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index f144f73d68..015930c0c0 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -36,7 +36,8 @@ -export([recvfrom/2, recvfrom/3]). -export([setopt/3, setopts/2, getopt/2, getopts/2, is_sockopt_val/2]). -export([chgopt/3, chgopts/2]). --export([getstat/2, getfd/1, getindex/1, getstatus/1, gettype/1, +-export([getstat/2, getfd/1, stealfd/1, returnfd/1, + getindex/1, getstatus/1, gettype/1, getifaddrs/1, getiflist/1, ifget/3, ifset/3, gethostname/1]). -export([getservbyname/3, getservbyport/3]). @@ -840,6 +841,28 @@ getfd(S) when is_port(S) -> {error,_}=Error -> Error end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% STEALFD(insock()) -> {ok,integer()} | {error, Reason} +%% +%% steal internal file descriptor +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +stealfd(S) when is_port(S) -> + getfd(S). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% RETURNFD(insock()) -> {ok,integer()} | {error, Reason} +%% +%% return internal file descriptor +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +returnfd(S) when is_port(S) -> + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% GETIX(insock()) -> {ok,integer()} | {error, Reason} -- cgit v1.2.3 From 0348a9c9c0114ddf83d776adc3d01ac60dfcccfc Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 25 Nov 2011 10:48:46 +0100 Subject: Implement sendfile using blocking io in asynch threads Move the command handling to outputv in preparation for header and trailer inclusion in the sendfile api. Use the standard efile communication functions for sendfile. --- erts/preloaded/src/prim_file.erl | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 606d7d5aab..0767067682 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -539,30 +539,22 @@ write_file(File, Bin) when (is_list(File) orelse is_binary(File)) -> end; write_file(_, _) -> {error, badarg}. - + %% Returns {error, Reason} | {ok, BytesCopied} -sendfile(_,_,_,_,_,_,_,_,_,_) -> - {error, enotsup}; +%sendfile(_,_,_,_,_,_,_,_,_,_) -> +% {error, enotsup}; sendfile(#file_descriptor{module = ?MODULE, data = {Port, _}}, DestFD, Offset, Bytes, ChunkSize, Headers, Trailers, Nodiskio, MNowait, Sync) -> - ok = drv_command(Port, <>), - Self = self(), - %% Should we use a ref()? - receive - {efile_reply, Self, Port, {ok, _Written}=OKRes}-> - OKRes; - {efile_reply, Self, Port, {error, _PosixError}=Error}-> - Error - end. + drv_command(Port, <>). get_bit(true) -> 1; @@ -578,8 +570,11 @@ encode_hdtl(List) -> encode_hdtl([], Acc, Cnt) -> <>; +encode_hdtl([Bin|T], Acc, Cnt) when is_binary(Bin) -> + encode_hdtl(T, <<(byte_size(Bin)):32, Bin/binary, Acc/binary>>,Cnt + 1); encode_hdtl([Bin|T], Acc, Cnt) -> - encode_hdtl(T, <<(byte_size(Bin)):32, Bin/binary, Acc/binary>>,Cnt + 1). + encode_hdtl(T, <<(iolist_size(Bin)):32, (iolist_to_binary(Bin))/binary, + Acc/binary>>,Cnt + 1). -- cgit v1.2.3 From 54bdd9a15d2e130c76f76ca322af56b306d02078 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 9 Nov 2011 11:52:14 +0100 Subject: Implement blocking calls for sendfile Move sendfile data to invoke data instead of file_descr. Remove usage of ready_output when doing a send. If told to send 0 bytes, file_sendfile now sends the entire file for linux. --- erts/preloaded/src/prim_file.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 0767067682..6bdf5f6e2e 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -547,12 +547,13 @@ write_file(_, _) -> sendfile(#file_descriptor{module = ?MODULE, data = {Port, _}}, DestFD, Offset, Bytes, ChunkSize, Headers, Trailers, Nodiskio, MNowait, Sync) -> - - drv_command(Port, <>). -- cgit v1.2.3 From 59e7e345ba51b7c2d6c9e479ce4cbb7c745c7893 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 25 Nov 2011 11:16:54 +0100 Subject: Implement ignorefd for TCP Ignore fd is a feature used by sendfile to temporarily remove all driver_select calls on that fd so that another driver can select on it. It also delays all actions which sends or receives data in that fd until in the fd is no longer ignored. Only the controlling_process should use the feature as it is otherwise possible that the ignore will never be cleaned up and hence create a memory leak in the driver. An ignored driver will not detect that an fd has been closed until it is unignored. --- erts/preloaded/src/prim_inet.erl | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index 015930c0c0..0cedd284db 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -36,7 +36,7 @@ -export([recvfrom/2, recvfrom/3]). -export([setopt/3, setopts/2, getopt/2, getopts/2, is_sockopt_val/2]). -export([chgopt/3, chgopts/2]). --export([getstat/2, getfd/1, stealfd/1, returnfd/1, +-export([getstat/2, getfd/1, ignorefd/2, getindex/1, getstatus/1, gettype/1, getifaddrs/1, getiflist/1, ifget/3, ifset/3, gethostname/1]). @@ -843,25 +843,18 @@ getfd(S) when is_port(S) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% -%% STEALFD(insock()) -> {ok,integer()} | {error, Reason} +%% IGNOREFD(insock(),boolean()) -> {ok,integer()} | {error, Reason} %% %% steal internal file descriptor %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -stealfd(S) when is_port(S) -> - getfd(S). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% -%% RETURNFD(insock()) -> {ok,integer()} | {error, Reason} -%% -%% return internal file descriptor -%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -returnfd(S) when is_port(S) -> - ok. +ignorefd(S,Bool) when is_port(S) -> + Val = if Bool -> 1; true -> 0 end, + case ctl_cmd(S, ?INET_REQ_IGNOREFD, [Val]) of + {ok, _} -> ok; + Error -> Error + end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% -- cgit v1.2.3 From a5b3d81936ab85edb8713f29baf85307ae0b25b8 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 25 Nov 2011 20:29:59 +0100 Subject: Preliminary work on header/trailer Have to figure out how to represent progress in header writing when using non-blocking, not sure how to do this. --- erts/preloaded/src/prim_file.erl | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 6bdf5f6e2e..fb19521382 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -547,38 +547,21 @@ write_file(_, _) -> sendfile(#file_descriptor{module = ?MODULE, data = {Port, _}}, DestFD, Offset, Bytes, ChunkSize, Headers, Trailers, Nodiskio, MNowait, Sync) -> - drv_command(Port, <>). + drv_command(Port, [<>, + Headers,Trailers]). get_bit(true) -> 1; get_bit(false) -> 0. -encode_hdtl(undefined) -> - <<0>>; -encode_hdtl([]) -> - <<0>>; -encode_hdtl(List) -> - encode_hdtl(List,<<>>,0). - -encode_hdtl([], Acc, Cnt) -> - <>; -encode_hdtl([Bin|T], Acc, Cnt) when is_binary(Bin) -> - encode_hdtl(T, <<(byte_size(Bin)):32, Bin/binary, Acc/binary>>,Cnt + 1); -encode_hdtl([Bin|T], Acc, Cnt) -> - encode_hdtl(T, <<(iolist_size(Bin)):32, (iolist_to_binary(Bin))/binary, - Acc/binary>>,Cnt + 1). - - - %%%----------------------------------------------------------------- -- cgit v1.2.3 From 4d198cb07025a8fc341a1e7fd7f9906b5fb714d6 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Sun, 27 Nov 2011 17:24:04 +0100 Subject: Only allow tcp sockets as target for sendfile --- erts/preloaded/src/prim_file.erl | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index fb19521382..6f35162feb 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -545,17 +545,24 @@ write_file(_, _) -> %sendfile(_,_,_,_,_,_,_,_,_,_) -> % {error, enotsup}; sendfile(#file_descriptor{module = ?MODULE, data = {Port, _}}, - DestFD, Offset, Bytes, ChunkSize, Headers, Trailers, - Nodiskio, MNowait, Sync) -> - drv_command(Port, [<>, - Headers,Trailers]). + Dest, Offset, Bytes, _ChunkSize, _Headers, _Trailers, + _Nodiskio, _MNowait, _Sync) -> + case erlang:port_get_data(Dest) of + Data when Data == inet_tcp; Data == inet6_tcp -> + ok = inet:lock_socket(Dest,true), + {ok, DestFD} = prim_inet:getfd(Dest), + try drv_command(Port, [<>]) + after + ok = inet:lock_socket(Dest,false) + end; + _Else -> + {error,badarg} + end. get_bit(true) -> 1; -- cgit v1.2.3 From 1bbf8cee44b8836d66d289cc0b5b314ed83de821 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 29 Nov 2011 11:05:37 +0100 Subject: Remove header/trailer support Since the API for headers/trailers seem to be very awkward to work with when using non-blocking io the feature is dropped for now. See unix_efile.c for more details. --- erts/preloaded/src/prim_file.erl | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 6f35162feb..7316e0be99 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -545,7 +545,7 @@ write_file(_, _) -> %sendfile(_,_,_,_,_,_,_,_,_,_) -> % {error, enotsup}; sendfile(#file_descriptor{module = ?MODULE, data = {Port, _}}, - Dest, Offset, Bytes, _ChunkSize, _Headers, _Trailers, + Dest, Offset, Bytes, _ChunkSize, Headers, Trailers, _Nodiskio, _MNowait, _Sync) -> case erlang:port_get_data(Dest) of Data when Data == inet_tcp; Data == inet6_tcp -> @@ -555,8 +555,9 @@ sendfile(#file_descriptor{module = ?MODULE, data = {Port, _}}, 0:8, Offset:64/unsigned, Bytes:64/unsigned, - 0:32/unsigned, - 0:32/unsigned>>]) + (iolist_size(Headers)):32/unsigned, + (iolist_size(Trailers)):32/unsigned>>, + Headers,Trailers]) after ok = inet:lock_socket(Dest,false) end; @@ -564,12 +565,6 @@ sendfile(#file_descriptor{module = ?MODULE, data = {Port, _}}, {error,badarg} end. -get_bit(true) -> - 1; -get_bit(false) -> - 0. - - %%%----------------------------------------------------------------- %%% Functions operating on files without handle to the file. ?DRV. -- cgit v1.2.3