| /* |
| |
| kHTTPd -- the next generation |
| |
| Send actual file-data to the connections |
| |
| */ |
| /**************************************************************** |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2, or (at your option) |
| * any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| * |
| ****************************************************************/ |
| |
| /* |
| |
| Purpose: |
| |
| DataSending does the actual sending of file-data to the socket. |
| |
| Note: Since asynchronous reads do not -yet- exists, this might block! |
| |
| Return value: |
| The number of requests that changed status (ie: made some progress) |
| */ |
| |
| #include <linux/config.h> |
| #include <linux/kernel.h> |
| #include <linux/locks.h> |
| #include <linux/skbuff.h> |
| |
| #include <net/tcp.h> |
| |
| #include <asm/uaccess.h> |
| #include <linux/smp_lock.h> |
| |
| #include "structure.h" |
| #include "prototypes.h" |
| |
| static char *Block[CONFIG_KHTTPD_NUMCPU]; |
| |
| /* |
| |
| This send_actor is for use with do_generic_file_read (ie sendfile()) |
| It sends the data to the socket indicated by desc->buf. |
| |
| */ |
| static int sock_send_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size) |
| { |
| int written; |
| char *kaddr; |
| unsigned long count = desc->count; |
| struct socket *sock = (struct socket *) desc->buf; |
| mm_segment_t old_fs; |
| |
| if (size > count) |
| size = count; |
| old_fs = get_fs(); |
| set_fs(KERNEL_DS); |
| |
| kaddr = kmap(page); |
| written = SendBuffer_async(sock, kaddr + offset, size); |
| kunmap(page); |
| set_fs(old_fs); |
| if (written < 0) { |
| desc->error = written; |
| written = 0; |
| } |
| desc->count = count - written; |
| desc->written += written; |
| return written; |
| } |
| |
| |
| |
| |
| int DataSending(const int CPUNR) |
| { |
| struct http_request *CurrentRequest,**Prev; |
| int count = 0; |
| |
| EnterFunction("DataSending"); |
| |
| Prev = &(threadinfo[CPUNR].DataSendingQueue); |
| CurrentRequest = threadinfo[CPUNR].DataSendingQueue; |
| while (CurrentRequest!=NULL) |
| { |
| int ReadSize,Space; |
| int retval; |
| |
| |
| /* First, test if the socket has any buffer-space left. |
| If not, no need to actually try to send something. */ |
| |
| |
| Space = sock_wspace(CurrentRequest->sock->sk); |
| |
| ReadSize = min_t(int, 4 * 4096, CurrentRequest->FileLength - CurrentRequest->BytesSent); |
| ReadSize = min_t(int, ReadSize, Space); |
| |
| if (ReadSize>0) |
| { |
| struct inode *inode; |
| |
| inode = CurrentRequest->filp->f_dentry->d_inode; |
| |
| if (inode->i_mapping->a_ops->readpage) { |
| /* This does the actual transfer using sendfile */ |
| read_descriptor_t desc; |
| loff_t *ppos; |
| |
| CurrentRequest->filp->f_pos = CurrentRequest->BytesSent; |
| |
| ppos = &CurrentRequest->filp->f_pos; |
| |
| desc.written = 0; |
| desc.count = ReadSize; |
| desc.buf = (char *) CurrentRequest->sock; |
| desc.error = 0; |
| do_generic_file_read(CurrentRequest->filp, ppos, &desc, sock_send_actor); |
| if (desc.written>0) |
| { |
| CurrentRequest->BytesSent += desc.written; |
| count++; |
| } |
| } |
| else /* FS doesn't support sendfile() */ |
| { |
| mm_segment_t oldfs; |
| CurrentRequest->filp->f_pos = CurrentRequest->BytesSent; |
| |
| oldfs = get_fs(); set_fs(KERNEL_DS); |
| retval = CurrentRequest->filp->f_op->read(CurrentRequest->filp, Block[CPUNR], ReadSize, &CurrentRequest->filp->f_pos); |
| set_fs(oldfs); |
| |
| if (retval>0) |
| { |
| retval = SendBuffer_async(CurrentRequest->sock,Block[CPUNR],(size_t)retval); |
| if (retval>0) |
| { |
| CurrentRequest->BytesSent += retval; |
| count++; |
| } |
| } |
| } |
| |
| } |
| |
| /* |
| If end-of-file or closed connection: Finish this request |
| by moving it to the "logging" queue. |
| */ |
| if ((CurrentRequest->BytesSent>=CurrentRequest->FileLength)|| |
| (CurrentRequest->sock->sk->state!=TCP_ESTABLISHED |
| && CurrentRequest->sock->sk->state!=TCP_CLOSE_WAIT)) |
| { |
| struct http_request *Next; |
| Next = CurrentRequest->Next; |
| |
| lock_sock(CurrentRequest->sock->sk); |
| if (CurrentRequest->sock->sk->state == TCP_ESTABLISHED || |
| CurrentRequest->sock->sk->state == TCP_CLOSE_WAIT) |
| { |
| CurrentRequest->sock->sk->tp_pinfo.af_tcp.nonagle = 0; |
| tcp_push_pending_frames(CurrentRequest->sock->sk,&(CurrentRequest->sock->sk->tp_pinfo.af_tcp)); |
| } |
| release_sock(CurrentRequest->sock->sk); |
| |
| (*Prev) = CurrentRequest->Next; |
| |
| CurrentRequest->Next = threadinfo[CPUNR].LoggingQueue; |
| threadinfo[CPUNR].LoggingQueue = CurrentRequest; |
| |
| CurrentRequest = Next; |
| continue; |
| |
| } |
| |
| |
| Prev = &(CurrentRequest->Next); |
| CurrentRequest = CurrentRequest->Next; |
| } |
| |
| LeaveFunction("DataSending"); |
| return count; |
| } |
| |
| int InitDataSending(int ThreadCount) |
| { |
| int I,I2; |
| |
| EnterFunction("InitDataSending"); |
| I=0; |
| while (I<ThreadCount) |
| { |
| Block[I] = (char*)get_free_page((int)GFP_KERNEL); |
| if (Block[I] == NULL) |
| { |
| I2=0; |
| while (I2<I-1) |
| { |
| free_page((unsigned long)Block[I2++]); |
| } |
| LeaveFunction("InitDataSending - abort"); |
| return -1; |
| } |
| I++; |
| } |
| LeaveFunction("InitDataSending"); |
| return 0; |
| } |
| |
| void StopDataSending(const int CPUNR) |
| { |
| struct http_request *CurrentRequest,*Next; |
| |
| EnterFunction("StopDataSending"); |
| CurrentRequest = threadinfo[CPUNR].DataSendingQueue; |
| |
| while (CurrentRequest!=NULL) |
| { |
| Next = CurrentRequest->Next; |
| CleanUpRequest(CurrentRequest); |
| CurrentRequest=Next; |
| } |
| |
| threadinfo[CPUNR].DataSendingQueue = NULL; |
| |
| free_page( (unsigned long)Block[CPUNR]); |
| LeaveFunction("StopDataSending"); |
| } |