20
20
* this program; if not, write to the Free Software Foundation, Inc., 59
21
21
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
22
*/
23
- #include <asm/i387.h>
24
23
#include <linux/freezer.h>
24
+ #include <linux/irq_work.h>
25
25
#include <linux/ipv6.h>
26
26
#include <linux/kthread.h>
27
27
#include <linux/tcp.h>
28
28
#include <linux/topology.h>
29
- #include <linux/workqueue.h>
30
29
31
30
#include "tdb.h"
32
31
33
32
#include "tempesta_fw.h"
34
33
#include "cache.h"
35
34
#include "http_msg.h"
36
35
#include "ss_skb.h"
36
+ #include "work_queue.h"
37
37
38
38
#if MAX_NUMNODES > ((1 << 16 ) - 1 )
39
39
#warning "Please set CONFIG_NODES_SHIFT to less than 16"
@@ -71,14 +71,19 @@ typedef struct {
71
71
#define TFW_CSTR_HDRLEN (sizeof(TfwCStr))
72
72
73
73
/* Work to copy response body to database. */
74
- typedef struct tfw_cache_work_t {
75
- struct work_struct work ;
74
+ typedef struct {
76
75
TfwHttpReq * req ;
77
76
TfwHttpResp * resp ;
78
77
tfw_http_cache_cb_t action ;
79
78
unsigned long key ;
80
79
} TfwCWork ;
81
80
81
+ typedef struct {
82
+ struct tasklet_struct tasklet ;
83
+ struct irq_work ipi_work ;
84
+ TfwRBQueue wq ;
85
+ } TfwWorkTasklet ;
86
+
82
87
static struct {
83
88
int cache ;
84
89
unsigned int db_size ;
@@ -114,8 +119,7 @@ static struct {
114
119
} c_nodes [MAX_NUMNODES ] __read_mostly ;
115
120
116
121
static struct task_struct * cache_mgr_thr ;
117
- static struct workqueue_struct * cache_wq ;
118
- static struct kmem_cache * c_cache ;
122
+ static DEFINE_PER_CPU (TfwWorkTasklet , cache_wq );
119
123
120
124
/* Iterate over request URI and Host header to process request key. */
121
125
#define TFW_CACHE_REQ_KEYITER (c , req , u_end , h_start , h_end ) \
@@ -137,31 +141,6 @@ static struct kmem_cache *c_cache;
137
141
} \
138
142
for ( ; c != h_end; ++c, c = (c == u_end) ? h_start : c)
139
143
140
-
141
- /**
142
- * Calculates search key for the request URI and Host header.
143
- *
144
- * The function can be called from sotrirq as well as from work queue.
145
- * Softirq saves FPU context, so we can execute tfw_http_req_key_calc()
146
- * w/o explicit FPU context saving.
147
- */
148
- static unsigned long
149
- tfw_cache_key_calc (TfwHttpReq * req )
150
- {
151
- unsigned long h ;
152
-
153
- if (likely (in_softirq ()))
154
- return tfw_http_req_key_calc (req );
155
-
156
- kernel_fpu_begin ();
157
-
158
- h = tfw_http_req_key_calc (req );
159
-
160
- kernel_fpu_end ();
161
-
162
- return h ;
163
- }
164
-
165
144
static bool
166
145
tfw_cache_entry_key_eq (TDB * db , TfwHttpReq * req , TfwCacheEntry * ce )
167
146
{
@@ -233,9 +212,10 @@ node_db(void)
233
212
234
213
/**
235
214
* Get a CPU identifier from @node to schedule a work.
215
+ * TODO do better CPU scheduling
236
216
*/
237
217
static int
238
- tfw_cache_sched_work_cpu (TfwHttpReq * req )
218
+ tfw_cache_sched_cpu (TfwHttpReq * req )
239
219
{
240
220
return c_nodes [req -> node ].cpu ;
241
221
}
@@ -446,7 +426,7 @@ tfw_cache_add(TfwHttpResp *resp, TfwHttpReq *req)
446
426
if (!cache_cfg .cache )
447
427
return ;
448
428
449
- key = tfw_cache_key_calc (req );
429
+ key = tfw_http_req_key_calc (req );
450
430
451
431
tot_len = sizeof (TfwCacheEntry )
452
432
+ req -> uri_path .len + req -> h_tbl -> tbl [TFW_HTTP_HDR_HOST ].len
@@ -467,38 +447,29 @@ tfw_cache_add(TfwHttpResp *resp, TfwHttpReq *req)
467
447
}
468
448
}
469
449
470
- static void
471
- tfw_cache_resp_process_node (struct work_struct * work )
472
- {
473
- TfwCWork * cw = (TfwCWork * )work ;
474
-
475
- cw -> action (cw -> req , cw -> resp );
476
-
477
- kmem_cache_free (c_cache , cw );
478
- }
479
-
480
- void
481
- tfw_cache_resp_process (TfwHttpResp * resp , TfwHttpReq * req ,
482
- tfw_http_cache_cb_t action )
450
+ int
451
+ tfw_cache_process (TfwHttpReq * req , TfwHttpResp * resp ,
452
+ tfw_http_cache_cb_t action )
483
453
{
484
- TfwCWork * cw ;
454
+ int cpu ;
455
+ TfwWorkTasklet * ct ;
456
+ TfwCWork cw ;
485
457
486
- if (cache_cfg .cache != TFW_CACHE_SHARD ) {
458
+ if (! cache_cfg .cache ) {
487
459
action (req , resp );
488
- return ;
460
+ return 0 ;
489
461
}
490
462
491
- cw = kmem_cache_alloc (c_cache , GFP_ATOMIC );
492
- if (!cw ) {
493
- action (req , resp );
494
- return ;
495
- }
496
- INIT_WORK (& cw -> work , tfw_cache_resp_process_node );
497
- cw -> req = req ;
498
- cw -> resp = resp ;
499
- cw -> action = action ;
500
- queue_work_on (tfw_cache_sched_work_cpu (req ), cache_wq ,
501
- (struct work_struct * )cw );
463
+ cw .req = req ;
464
+ cw .resp = resp ;
465
+ cw .action = action ;
466
+ cw .key = tfw_http_req_key_calc (req );
467
+ req -> node = (cache_cfg .cache == TFW_CACHE_SHARD )
468
+ ? tfw_cache_key_node (cw .key )
469
+ : numa_node_id ();
470
+ cpu = tfw_cache_sched_cpu (req );
471
+ ct = & per_cpu (cache_wq , cpu );
472
+ return tfw_wq_push (& ct -> wq , & cw , cpu , & ct -> ipi_work );
502
473
}
503
474
504
475
static int
@@ -689,7 +660,7 @@ tfw_cache_build_resp(TfwCacheEntry *ce)
689
660
}
690
661
691
662
static void
692
- __cache_req_process_node (TfwHttpReq * req , unsigned long key ,
663
+ cache_req_process_node (TfwHttpReq * req , unsigned long key ,
693
664
tfw_http_cache_cb_t action )
694
665
{
695
666
TfwCacheEntry * ce = NULL ;
@@ -720,55 +691,25 @@ __cache_req_process_node(TfwHttpReq *req, unsigned long key,
720
691
}
721
692
722
693
static void
723
- tfw_cache_req_process_node ( struct work_struct * work )
694
+ tfw_wq_tasklet ( unsigned long data )
724
695
{
725
- TfwCWork * cw = (TfwCWork * )work ;
726
-
727
- __cache_req_process_node (cw -> req , cw -> key , cw -> action );
728
-
729
- kmem_cache_free (c_cache , cw );
696
+ TfwWorkTasklet * ct = (TfwWorkTasklet * )data ;
697
+ TfwCWork cw ;
698
+
699
+ while (!tfw_wq_pop (& ct -> wq , & cw )) {
700
+ if (!cache_cfg .cache || cw .resp )
701
+ cw .action (cw .req , cw .resp );
702
+ else
703
+ cache_req_process_node (cw .req , cw .key , cw .action );
704
+ }
730
705
}
731
706
732
- /**
733
- * Process @req at node which possesses the cached data required to fulfill
734
- * the request. In worse case the request can be assembled in softirq at
735
- * one node and the cached response can be prepared at the second node.
736
- * Note that RFS + XFS are responsible for processing the same socket only
737
- * at one CPU, so response is always sent through the same CPU where a request
738
- * was received.
739
- */
740
- void
741
- tfw_cache_req_process (TfwHttpReq * req , tfw_http_cache_cb_t action )
707
+ static void
708
+ tfw_cache_ipi (struct irq_work * work )
742
709
{
743
- unsigned long key ;
744
-
745
- if (!cache_cfg .cache ) {
746
- action (req , NULL );
747
- return ;
748
- }
749
-
750
- key = tfw_cache_key_calc (req );
751
- req -> node = tfw_cache_key_node (key );
752
-
753
- if (cache_cfg .cache == TFW_CACHE_REPLICA )
754
- goto process_locally ;
755
-
756
- if (req -> node != numa_node_id ()) {
757
- /* Schedule the cache entry to the right node. */
758
- TfwCWork * cw = kmem_cache_alloc (c_cache , GFP_ATOMIC );
759
- if (!cw )
760
- goto process_locally ;
761
- INIT_WORK (& cw -> work , tfw_cache_req_process_node );
762
- cw -> req = req ;
763
- cw -> action = action ;
764
- cw -> key = key ;
765
- queue_work_on (tfw_cache_sched_work_cpu (req ), cache_wq ,
766
- (struct work_struct * )cw );
767
- return ;
768
- }
710
+ TfwWorkTasklet * ct = container_of (work , TfwWorkTasklet , ipi_work );
769
711
770
- process_locally :
771
- __cache_req_process_node (req , key , action );
712
+ tasklet_schedule (& ct -> tasklet );
772
713
}
773
714
774
715
/**
@@ -799,15 +740,15 @@ tfw_cache_mgr(void *arg)
799
740
static int
800
741
tfw_cache_start (void )
801
742
{
802
- int nid , r = 1 ;
743
+ int i , r = 1 ;
803
744
804
745
if (!cache_cfg .cache )
805
746
return 0 ;
806
747
807
- for_each_node_with_cpus (nid ) {
808
- c_nodes [nid ].db = tdb_open (cache_cfg .db_path ,
809
- cache_cfg .db_size , 0 , nid );
810
- if (!c_nodes [nid ].db )
748
+ for_each_node_with_cpus (i ) {
749
+ c_nodes [i ].db = tdb_open (cache_cfg .db_path ,
750
+ cache_cfg .db_size , 0 , i );
751
+ if (!c_nodes [i ].db )
811
752
goto close_db ;
812
753
}
813
754
@@ -818,52 +759,40 @@ tfw_cache_start(void)
818
759
goto close_db ;
819
760
}
820
761
821
- c_cache = KMEM_CACHE (tfw_cache_work_t , 0 );
822
- if (!c_cache )
823
- goto err_cache ;
824
-
825
762
tfw_init_node_cpus ();
826
763
827
- /*
828
- * WQ_MEM_RECLAIM - allow the workers to do progress
829
- * in memory preasure;
830
- * WQ_UNBOUND - schedule work to particular node and
831
- * let the system scheduler to scheduler worker
832
- * among node cpus.
833
- *
834
- * TODO use tasklets and TfwWorkQueue for cache processing
835
- * Probably threading should be at TDB side...
836
- */
837
- cache_wq = alloc_workqueue ("tfw_cache_wq" ,
838
- WQ_MEM_RECLAIM | WQ_UNBOUND , 0 );
839
- if (!cache_wq )
840
- goto err_wq ;
764
+ TFW_WQ_CHECKSZ (TfwCWork );
765
+ for_each_online_cpu (i ) {
766
+ TfwWorkTasklet * ct = & per_cpu (cache_wq , i );
767
+ tfw_wq_init (& ct -> wq , cpu_to_node (i ));
768
+ init_irq_work (& ct -> ipi_work , tfw_cache_ipi );
769
+ tasklet_init (& ct -> tasklet , tfw_wq_tasklet , (unsigned long )ct );
770
+ }
841
771
842
772
return 0 ;
843
- err_wq :
844
- kmem_cache_destroy (c_cache );
845
- err_cache :
846
- kthread_stop (cache_mgr_thr );
847
773
close_db :
848
- for_each_node_with_cpus (nid )
849
- tdb_close (c_nodes [nid ].db );
774
+ for_each_node_with_cpus (i )
775
+ tdb_close (c_nodes [i ].db );
850
776
return r ;
851
777
}
852
778
853
779
static void
854
780
tfw_cache_stop (void )
855
781
{
856
- int nid ;
782
+ int i ;
857
783
858
784
if (!cache_cfg .cache )
859
785
return ;
860
786
861
- destroy_workqueue (cache_wq );
862
- kmem_cache_destroy (c_cache );
787
+ for_each_online_cpu (i ) {
788
+ TfwWorkTasklet * ct = & per_cpu (cache_wq , i );
789
+ tfw_wq_destroy (& ct -> wq );
790
+ tasklet_kill (& ct -> tasklet );
791
+ }
863
792
kthread_stop (cache_mgr_thr );
864
793
865
- for_each_node_with_cpus (nid )
866
- tdb_close (c_nodes [nid ].db );
794
+ for_each_node_with_cpus (i )
795
+ tdb_close (c_nodes [i ].db );
867
796
}
868
797
869
798
static TfwCfgSpec tfw_cache_cfg_specs [] = {
0 commit comments