Whamcloud - gitweb
* Landing 12014: ASSERTION failures when upgrading to the patchless zero-copy
[fs/lustre-release.git] / lnet / klnds / socklnd / socklnd_lib-linux.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  */
4
5 #include "socklnd.h"
6
7 # if CONFIG_SYSCTL && !CFS_SYSFS_MODULE_PARM
8 static cfs_sysctl_table_t ksocknal_ctl_table[21];
9
10 cfs_sysctl_table_t ksocknal_top_ctl_table[] = {
11         {
12                 .ctl_name = 200,
13                 .procname = "socknal",
14                 .data     = NULL,
15                 .maxlen   = 0,
16                 .mode     = 0555,
17                 .child    = ksocknal_ctl_table
18         },
19         { 0 }
20 };
21
22 int
23 ksocknal_lib_tunables_init ()
24 {
25         int    i = 0;
26         int    j = 1;
27
28         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
29                 .ctl_name = j++,
30                 .procname = "timeout",
31                 .data     = ksocknal_tunables.ksnd_timeout,
32                 .maxlen   = sizeof (int),
33                 .mode     = 0644,
34                 .proc_handler = &proc_dointvec
35         };
36         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
37                 .ctl_name = j++,
38                 .procname = "credits",
39                 .data     = ksocknal_tunables.ksnd_credits,
40                 .maxlen   = sizeof (int),
41                 .mode     = 0444,
42                 .proc_handler = &proc_dointvec
43         };
44         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
45                 .ctl_name = j++,
46                 .procname = "peer_credits",
47                 .data     = ksocknal_tunables.ksnd_peercredits,
48                 .maxlen   = sizeof (int),
49                 .mode     = 0444,
50                 .proc_handler = &proc_dointvec
51         };
52         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
53                 .ctl_name = j++,
54                 .procname = "nconnds",
55                 .data     = ksocknal_tunables.ksnd_nconnds,
56                 .maxlen   = sizeof (int),
57                 .mode     = 0444,
58                 .proc_handler = &proc_dointvec
59         };
60         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
61                 .ctl_name = j++,
62                 .procname = "min_reconnectms",
63                 .data     = ksocknal_tunables.ksnd_min_reconnectms,
64                 .maxlen   = sizeof (int),
65                 .mode     = 0444,
66                 .proc_handler = &proc_dointvec
67         };
68         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
69                 .ctl_name = j++,
70                 .procname = "max_reconnectms",
71                 .data     = ksocknal_tunables.ksnd_max_reconnectms,
72                 .maxlen   = sizeof (int),
73                 .mode     = 0444,
74                 .proc_handler = &proc_dointvec
75         };
76         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
77                 .ctl_name = j++,
78                 .procname = "eager_ack",
79                 .data     = ksocknal_tunables.ksnd_eager_ack,
80                 .maxlen   = sizeof (int),
81                 .mode     = 0644,
82                 .proc_handler = &proc_dointvec
83         };
84         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
85                 .ctl_name = j++,
86                 .procname = "zero_copy",
87                 .data     = ksocknal_tunables.ksnd_zc_min_frag,
88                 .maxlen   = sizeof (int),
89                 .mode     = 0644,
90                 .proc_handler = &proc_dointvec
91         };
92         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
93                 .ctl_name = j++,
94                 .procname = "typed",
95                 .data     = ksocknal_tunables.ksnd_typed_conns,
96                 .maxlen   = sizeof (int),
97                 .mode     = 0444,
98                 .proc_handler = &proc_dointvec
99         };
100         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
101                 .ctl_name = j++,
102                 .procname = "min_bulk",
103                 .data     = ksocknal_tunables.ksnd_min_bulk,
104                 .maxlen   = sizeof (int),
105                 .mode     = 0644,
106                 .proc_handler = &proc_dointvec
107         };
108         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
109                 .ctl_name = j++,
110                 .procname = "rx_buffer_size",
111                 .data     = ksocknal_tunables.ksnd_rx_buffer_size,
112                 .maxlen   = sizeof(int),
113                 .mode     = 0644,
114                 .proc_handler = &proc_dointvec
115         };
116         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
117                 .ctl_name = j++,
118                 .procname = "tx_buffer_size",
119                 .data     = ksocknal_tunables.ksnd_tx_buffer_size,
120                 .maxlen   = sizeof(int),
121                 .mode     = 0644,
122                 .proc_handler = &proc_dointvec
123         };
124         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
125                 .ctl_name = j++,
126                 .procname = "nagle",
127                 .data     = ksocknal_tunables.ksnd_nagle,
128                 .maxlen   = sizeof(int),
129                 .mode     = 0644,
130                 .proc_handler = &proc_dointvec
131         };
132 #if CPU_AFFINITY
133         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
134                 .ctl_name = j++,
135                 .procname = "irq_affinity",
136                 .data     = ksocknal_tunables.ksnd_irq_affinity,
137                 .maxlen   = sizeof(int),
138                 .mode     = 0644,
139                 .proc_handler = &proc_dointvec
140         };
141 #endif
142         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
143                 .ctl_name = j++,
144                 .procname = "keepalive_idle",
145                 .data     = ksocknal_tunables.ksnd_keepalive_idle,
146                 .maxlen   = sizeof(int),
147                 .mode     = 0644,
148                 .proc_handler = &proc_dointvec
149         };
150         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
151                 .ctl_name = j++,
152                 .procname = "keepalive_count",
153                 .data     = ksocknal_tunables.ksnd_keepalive_count,
154                 .maxlen   = sizeof(int),
155                 .mode     = 0644,
156                 .proc_handler = &proc_dointvec
157         };
158         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
159                 .ctl_name = j++,
160                 .procname = "keepalive_intvl",
161                 .data     = ksocknal_tunables.ksnd_keepalive_intvl,
162                 .maxlen   = sizeof(int),
163                 .mode     = 0644,
164                 .proc_handler = &proc_dointvec
165         };
166 #ifdef SOCKNAL_BACKOFF
167         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
168                 .ctl_name = j++,
169                 .procname = "backoff_init",
170                 .data     = ksocknal_tunables.ksnd_backoff_init,
171                 .maxlen   = sizeof(int),
172                 .mode     = 0644,
173                 .proc_handler = &proc_dointvec
174         };
175         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
176                 .ctl_name = j++,
177                 .procname = "backoff_max",
178                 .data     = ksocknal_tunables.ksnd_backoff_max,
179                 .maxlen   = sizeof(int),
180                 .mode     = 0644,
181                 .proc_handler = &proc_dointvec
182         };
183 #endif
184 #if SOCKNAL_VERSION_DEBUG
185         ksocknal_ctl_table[i++] = (cfs_sysctl_table_t) {
186                 .ctl_name = j++,
187                 .procname = "protocol",
188                 .data     = ksocknal_tunables.ksnd_protocol,
189                 .maxlin   = sizeof(int),
190                 .mode     = 0644,
191                 .proc_handler = &proc_dointvec
192         };
193 #endif
194         LASSERT (j == i+1);
195         LASSERT (i < sizeof(ksocknal_ctl_table)/sizeof(ksocknal_ctl_table[0]));
196
197         ksocknal_tunables.ksnd_sysctl =
198                 cfs_register_sysctl_table(ksocknal_top_ctl_table, 0);
199
200         if (ksocknal_tunables.ksnd_sysctl == NULL)
201                 CWARN("Can't setup /proc tunables\n");
202
203         return 0;
204 }
205
206 void
207 ksocknal_lib_tunables_fini ()
208 {
209         if (ksocknal_tunables.ksnd_sysctl != NULL)
210                 cfs_unregister_sysctl_table(ksocknal_tunables.ksnd_sysctl);
211 }
212 #else
213 int
214 ksocknal_lib_tunables_init ()
215 {
216         return 0;
217 }
218
219 void
220 ksocknal_lib_tunables_fini ()
221 {
222 }
223 #endif /* # if CONFIG_SYSCTL && !CFS_SYSFS_MODULE_PARM */
224
225 void
226 ksocknal_lib_bind_irq (unsigned int irq)
227 {
228 #if (defined(CONFIG_SMP) && CPU_AFFINITY)
229         int              bind;
230         int              cpu;
231         char             cmdline[64];
232         ksock_irqinfo_t *info;
233         char            *argv[] = {"/bin/sh",
234                                    "-c",
235                                    cmdline,
236                                    NULL};
237         char            *envp[] = {"HOME=/",
238                                    "PATH=/sbin:/bin:/usr/sbin:/usr/bin",
239                                    NULL};
240
241         LASSERT (irq < NR_IRQS);
242         if (irq == 0)              /* software NIC or affinity disabled */
243                 return;
244
245         info = &ksocknal_data.ksnd_irqinfo[irq];
246
247         write_lock_bh (&ksocknal_data.ksnd_global_lock);
248
249         LASSERT (info->ksni_valid);
250         bind = !info->ksni_bound;
251         info->ksni_bound = 1;
252
253         write_unlock_bh (&ksocknal_data.ksnd_global_lock);
254
255         if (!bind)                              /* bound already */
256                 return;
257
258         cpu = ksocknal_irqsched2cpu(info->ksni_sched);
259         snprintf (cmdline, sizeof (cmdline),
260                   "echo %d > /proc/irq/%u/smp_affinity", 1 << cpu, irq);
261
262         LCONSOLE_INFO("Binding irq %u to CPU %d with cmd: %s\n",
263                       irq, cpu, cmdline);
264
265         /* FIXME: Find a better method of setting IRQ affinity...
266          */
267
268         USERMODEHELPER(argv[0], argv, envp);
269 #endif
270 }
271
272 int
273 ksocknal_lib_get_conn_addrs (ksock_conn_t *conn)
274 {
275         int rc = libcfs_sock_getaddr(conn->ksnc_sock, 1,
276                                      &conn->ksnc_ipaddr,
277                                      &conn->ksnc_port);
278
279         /* Didn't need the {get,put}connsock dance to deref ksnc_sock... */
280         LASSERT (!conn->ksnc_closing);
281
282         if (rc != 0) {
283                 CERROR ("Error %d getting sock peer IP\n", rc);
284                 return rc;
285         }
286
287         rc = libcfs_sock_getaddr(conn->ksnc_sock, 0,
288                                  &conn->ksnc_myipaddr, NULL);
289         if (rc != 0) {
290                 CERROR ("Error %d getting sock local IP\n", rc);
291                 return rc;
292         }
293
294         return 0;
295 }
296
297 unsigned int
298 ksocknal_lib_sock_irq (struct socket *sock)
299 {
300         int                irq = 0;
301 #if CPU_AFFINITY
302         struct dst_entry  *dst;
303
304         if (!*ksocknal_tunables.ksnd_irq_affinity)
305                 return 0;
306
307         dst = sk_dst_get (sock->sk);
308         if (dst != NULL) {
309                 if (dst->dev != NULL) {
310                         irq = dst->dev->irq;
311                         if (irq >= NR_IRQS) {
312                                 CERROR ("Unexpected IRQ %x\n", irq);
313                                 irq = 0;
314                         }
315                 }
316                 dst_release (dst);
317         }
318
319 #endif
320         return irq;
321 }
322
323 int
324 ksocknal_lib_zc_capable(struct socket *sock)
325 {
326         int  caps = sock->sk->sk_route_caps;
327
328         /* ZC if the socket supports scatter/gather and doesn't need software
329          * checksums */
330         return ((caps & NETIF_F_SG) != 0 &&
331                 (caps & (NETIF_F_IP_CSUM | NETIF_F_NO_CSUM | NETIF_F_HW_CSUM)) != 0);
332 }
333
334 int
335 ksocknal_lib_send_iov (ksock_conn_t *conn, ksock_tx_t *tx)
336 {
337         struct socket *sock = conn->ksnc_sock;
338         int            nob;
339         int            rc;
340
341         if (*ksocknal_tunables.ksnd_enable_csum        && /* checksum enabled */
342             conn->ksnc_proto == &ksocknal_protocol_v2x && /* V2.x connection  */
343             tx->tx_nob == tx->tx_resid                 && /* frist sending    */
344             tx->tx_msg.ksm_csum == 0)                     /* not checksummed  */
345                 ksocknal_lib_csum_tx(tx);
346
347         /* NB we can't trust socket ops to either consume our iovs
348          * or leave them alone. */
349
350         {
351 #if SOCKNAL_SINGLE_FRAG_TX
352                 struct iovec    scratch;
353                 struct iovec   *scratchiov = &scratch;
354                 unsigned int    niov = 1;
355 #else
356                 struct iovec   *scratchiov = conn->ksnc_tx_scratch_iov;
357                 unsigned int    niov = tx->tx_niov;
358 #endif
359                 struct msghdr msg = {
360                         .msg_name       = NULL,
361                         .msg_namelen    = 0,
362                         .msg_iov        = scratchiov,
363                         .msg_iovlen     = niov,
364                         .msg_control    = NULL,
365                         .msg_controllen = 0,
366                         .msg_flags      = MSG_DONTWAIT
367                 };
368                 mm_segment_t oldmm = get_fs();
369                 int  i;
370
371                 for (nob = i = 0; i < niov; i++) {
372                         scratchiov[i] = tx->tx_iov[i];
373                         nob += scratchiov[i].iov_len;
374                 }
375
376                 if (!list_empty(&conn->ksnc_tx_queue) ||
377                     nob < tx->tx_resid)
378                         msg.msg_flags |= MSG_MORE;
379
380                 set_fs (KERNEL_DS);
381                 rc = sock_sendmsg(sock, &msg, nob);
382                 set_fs (oldmm);
383         }
384         return rc;
385 }
386
387 int
388 ksocknal_lib_send_kiov (ksock_conn_t *conn, ksock_tx_t *tx)
389 {
390         struct socket *sock = conn->ksnc_sock;
391         lnet_kiov_t    *kiov = tx->tx_kiov;
392         int            rc;
393         int            nob;
394
395         /* NB we can't trust socket ops to either consume our iovs
396          * or leave them alone. */
397
398         if (kiov->kiov_len >= *ksocknal_tunables.ksnd_zc_min_frag &&
399             tx->tx_msg.ksm_zc_req_cookie != 0) {
400                 /* Zero copy is enabled */
401                 struct page   *page = kiov->kiov_page;
402                 int            offset = kiov->kiov_offset;
403                 int            fragsize = kiov->kiov_len;
404                 int            msgflg = MSG_DONTWAIT;
405
406                 CDEBUG(D_NET, "page %p + offset %x for %d\n",
407                                page, offset, kiov->kiov_len);
408
409                 if (!list_empty(&conn->ksnc_tx_queue) ||
410                     fragsize < tx->tx_resid)
411                         msgflg |= MSG_MORE;
412
413                 rc = tcp_sendpage(sock, page, offset, fragsize, msgflg);
414         } else {
415 #if SOCKNAL_SINGLE_FRAG_TX || !SOCKNAL_RISK_KMAP_DEADLOCK
416                 struct iovec  scratch;
417                 struct iovec *scratchiov = &scratch;
418                 unsigned int  niov = 1;
419 #else
420 #ifdef CONFIG_HIGHMEM
421 #warning "XXX risk of kmap deadlock on multiple frags..."
422 #endif
423                 struct iovec *scratchiov = conn->ksnc_tx_scratch_iov;
424                 unsigned int  niov = tx->tx_nkiov;
425 #endif
426                 struct msghdr msg = {
427                         .msg_name       = NULL,
428                         .msg_namelen    = 0,
429                         .msg_iov        = scratchiov,
430                         .msg_iovlen     = niov,
431                         .msg_control    = NULL,
432                         .msg_controllen = 0,
433                         .msg_flags      = MSG_DONTWAIT
434                 };
435                 mm_segment_t  oldmm = get_fs();
436                 int           i;
437
438                 for (nob = i = 0; i < niov; i++) {
439                         scratchiov[i].iov_base = kmap(kiov[i].kiov_page) +
440                                                  kiov[i].kiov_offset;
441                         nob += scratchiov[i].iov_len = kiov[i].kiov_len;
442                 }
443
444                 if (!list_empty(&conn->ksnc_tx_queue) ||
445                     nob < tx->tx_resid)
446                         msg.msg_flags |= MSG_MORE;
447
448                 set_fs (KERNEL_DS);
449                 rc = sock_sendmsg(sock, &msg, nob);
450                 set_fs (oldmm);
451
452                 for (i = 0; i < niov; i++)
453                         kunmap(kiov[i].kiov_page);
454         }
455         return rc;
456 }
457
458 void
459 ksocknal_lib_eager_ack (ksock_conn_t *conn)
460 {
461         int            opt = 1;
462         mm_segment_t   oldmm = get_fs();
463         struct socket *sock = conn->ksnc_sock;
464
465         /* Remind the socket to ACK eagerly.  If I don't, the socket might
466          * think I'm about to send something it could piggy-back the ACK
467          * on, introducing delay in completing zero-copy sends in my
468          * peer. */
469
470         set_fs(KERNEL_DS);
471         sock->ops->setsockopt (sock, SOL_TCP, TCP_QUICKACK,
472                                (char *)&opt, sizeof (opt));
473         set_fs(oldmm);
474 }
475
476 int
477 ksocknal_lib_recv_iov (ksock_conn_t *conn)
478 {
479 #if SOCKNAL_SINGLE_FRAG_RX
480         struct iovec  scratch;
481         struct iovec *scratchiov = &scratch;
482         unsigned int  niov = 1;
483 #else
484         struct iovec *scratchiov = conn->ksnc_rx_scratch_iov;
485         unsigned int  niov = conn->ksnc_rx_niov;
486 #endif
487         struct iovec *iov = conn->ksnc_rx_iov;
488         struct msghdr msg = {
489                 .msg_name       = NULL,
490                 .msg_namelen    = 0,
491                 .msg_iov        = scratchiov,
492                 .msg_iovlen     = niov,
493                 .msg_control    = NULL,
494                 .msg_controllen = 0,
495                 .msg_flags      = 0
496         };
497         mm_segment_t oldmm = get_fs();
498         int          nob;
499         int          i;
500         int          rc;
501         int          fragnob;
502         int          sum;
503         __u32        saved_csum;
504
505         /* NB we can't trust socket ops to either consume our iovs
506          * or leave them alone. */
507         LASSERT (niov > 0);
508
509         for (nob = i = 0; i < niov; i++) {
510                 scratchiov[i] = iov[i];
511                 nob += scratchiov[i].iov_len;
512         }
513         LASSERT (nob <= conn->ksnc_rx_nob_wanted);
514
515         set_fs (KERNEL_DS);
516         rc = sock_recvmsg (conn->ksnc_sock, &msg, nob, MSG_DONTWAIT);
517         /* NB this is just a boolean..........................^ */
518         set_fs (oldmm);
519
520         saved_csum = 0;
521         if (conn->ksnc_proto == &ksocknal_protocol_v2x) {
522                 saved_csum = conn->ksnc_msg.ksm_csum;
523                 conn->ksnc_msg.ksm_csum = 0;
524         }
525
526         if (saved_csum != 0) {
527                 /* accumulate checksum */
528                 for (i = 0, sum = rc; sum > 0; i++, sum -= fragnob) {
529                         LASSERT (i < niov);
530
531                         fragnob = iov[i].iov_len;
532                         if (fragnob > sum)
533                                 fragnob = sum;
534
535                         conn->ksnc_rx_csum = ksocknal_csum(conn->ksnc_rx_csum,
536                                                            iov[i].iov_base, fragnob);
537                 }
538                 conn->ksnc_msg.ksm_csum = saved_csum;
539         }
540
541         return rc;
542 }
543
544 int
545 ksocknal_lib_recv_kiov (ksock_conn_t *conn)
546 {
547 #if SOCKNAL_SINGLE_FRAG_RX || !SOCKNAL_RISK_KMAP_DEADLOCK
548         struct iovec  scratch;
549         struct iovec *scratchiov = &scratch;
550         unsigned int  niov = 1;
551 #else
552 #ifdef CONFIG_HIGHMEM
553 #warning "XXX risk of kmap deadlock on multiple frags..."
554 #endif
555         struct iovec *scratchiov = conn->ksnc_rx_scratch_iov;
556         unsigned int  niov = conn->ksnc_rx_nkiov;
557 #endif
558         lnet_kiov_t   *kiov = conn->ksnc_rx_kiov;
559         struct msghdr msg = {
560                 .msg_name       = NULL,
561                 .msg_namelen    = 0,
562                 .msg_iov        = scratchiov,
563                 .msg_iovlen     = niov,
564                 .msg_control    = NULL,
565                 .msg_controllen = 0,
566                 .msg_flags      = 0
567         };
568         mm_segment_t oldmm = get_fs();
569         int          nob;
570         int          i;
571         int          rc;
572         void        *base;
573         int          sum;
574         int          fragnob;
575
576         /* NB we can't trust socket ops to either consume our iovs
577          * or leave them alone. */
578         for (nob = i = 0; i < niov; i++) {
579                 scratchiov[i].iov_base = kmap(kiov[i].kiov_page) + kiov[i].kiov_offset;
580                 nob += scratchiov[i].iov_len = kiov[i].kiov_len;
581         }
582         LASSERT (nob <= conn->ksnc_rx_nob_wanted);
583
584         set_fs (KERNEL_DS);
585         rc = sock_recvmsg (conn->ksnc_sock, &msg, nob, MSG_DONTWAIT);
586         /* NB this is just a boolean.......................^ */
587         set_fs (oldmm);
588
589         if (conn->ksnc_msg.ksm_csum != 0) {
590                 for (i = 0, sum = rc; sum > 0; i++, sum -= fragnob) {
591                         LASSERT (i < niov);
592
593                         /* Dang! have to kmap again because I have nowhere to stash the
594                          * mapped address.  But by doing it while the page is still
595                          * mapped, the kernel just bumps the map count and returns me
596                          * the address it stashed. */
597                         base = kmap(kiov[i].kiov_page) + kiov[i].kiov_offset;
598                         fragnob = kiov[i].kiov_len;
599                         if (fragnob > sum)
600                                 fragnob = sum;
601
602                         conn->ksnc_rx_csum = ksocknal_csum(conn->ksnc_rx_csum,
603                                                            base, fragnob);
604
605                         kunmap(kiov[i].kiov_page);
606                 }
607         }
608         for (i = 0; i < niov; i++)
609                 kunmap(kiov[i].kiov_page);
610
611         return (rc);
612 }
613
614 void ksocknal_lib_csum_tx(ksock_tx_t *tx)
615 {
616         int          i;
617         __u32        csum;
618         void        *base;
619
620         LASSERT(tx->tx_iov[0].iov_base == (void *)&tx->tx_msg);
621         LASSERT(tx->tx_conn != NULL);
622         LASSERT(tx->tx_conn->ksnc_proto == &ksocknal_protocol_v2x);
623
624         tx->tx_msg.ksm_csum = 0;
625
626         csum = ksocknal_csum(~0, (void *)tx->tx_iov[0].iov_base,
627                              tx->tx_iov[0].iov_len);
628
629         if (tx->tx_kiov != NULL) {
630                 for (i = 0; i < tx->tx_nkiov; i++) {
631                         base = kmap(tx->tx_kiov[i].kiov_page) +
632                                tx->tx_kiov[i].kiov_offset;
633
634                         csum = ksocknal_csum(csum, base, tx->tx_kiov[i].kiov_len);
635
636                         kunmap(tx->tx_kiov[i].kiov_page);
637                 }
638         } else {
639                 for (i = 1; i < tx->tx_niov; i++)
640                         csum = ksocknal_csum(csum, tx->tx_iov[i].iov_base,
641                                              tx->tx_iov[i].iov_len);
642         }
643
644         if (*ksocknal_tunables.ksnd_inject_csum_error) {
645                 csum++;
646                 *ksocknal_tunables.ksnd_inject_csum_error = 0;
647         }
648
649         tx->tx_msg.ksm_csum = csum;
650 }
651
652 int
653 ksocknal_lib_get_conn_tunables (ksock_conn_t *conn, int *txmem, int *rxmem, int *nagle)
654 {
655         mm_segment_t   oldmm = get_fs ();
656         struct socket *sock = conn->ksnc_sock;
657         int            len;
658         int            rc;
659
660         rc = ksocknal_connsock_addref(conn);
661         if (rc != 0) {
662                 LASSERT (conn->ksnc_closing);
663                 *txmem = *rxmem = *nagle = 0;
664                 return (-ESHUTDOWN);
665         }
666
667         rc = libcfs_sock_getbuf(sock, txmem, rxmem);
668         if (rc == 0) {
669                 len = sizeof(*nagle);
670                 set_fs(KERNEL_DS);
671                 rc = sock->ops->getsockopt(sock, SOL_TCP, TCP_NODELAY,
672                                            (char *)nagle, &len);
673                 set_fs(oldmm);
674         }
675
676         ksocknal_connsock_decref(conn);
677
678         if (rc == 0)
679                 *nagle = !*nagle;
680         else
681                 *txmem = *rxmem = *nagle = 0;
682
683         return (rc);
684 }
685
686 int
687 ksocknal_lib_setup_sock (struct socket *sock)
688 {
689         mm_segment_t    oldmm = get_fs ();
690         int             rc;
691         int             option;
692         int             keep_idle;
693         int             keep_intvl;
694         int             keep_count;
695         int             do_keepalive;
696         struct linger   linger;
697
698         sock->sk->sk_allocation = GFP_NOFS;
699
700         /* Ensure this socket aborts active sends immediately when we close
701          * it. */
702
703         linger.l_onoff = 0;
704         linger.l_linger = 0;
705
706         set_fs (KERNEL_DS);
707         rc = sock_setsockopt (sock, SOL_SOCKET, SO_LINGER,
708                               (char *)&linger, sizeof (linger));
709         set_fs (oldmm);
710         if (rc != 0) {
711                 CERROR ("Can't set SO_LINGER: %d\n", rc);
712                 return (rc);
713         }
714
715         option = -1;
716         set_fs (KERNEL_DS);
717         rc = sock->ops->setsockopt (sock, SOL_TCP, TCP_LINGER2,
718                                     (char *)&option, sizeof (option));
719         set_fs (oldmm);
720         if (rc != 0) {
721                 CERROR ("Can't set SO_LINGER2: %d\n", rc);
722                 return (rc);
723         }
724
725         if (!*ksocknal_tunables.ksnd_nagle) {
726                 option = 1;
727
728                 set_fs (KERNEL_DS);
729                 rc = sock->ops->setsockopt (sock, SOL_TCP, TCP_NODELAY,
730                                             (char *)&option, sizeof (option));
731                 set_fs (oldmm);
732                 if (rc != 0) {
733                         CERROR ("Can't disable nagle: %d\n", rc);
734                         return (rc);
735                 }
736         }
737
738         rc = libcfs_sock_setbuf(sock,
739                                 *ksocknal_tunables.ksnd_tx_buffer_size,
740                                 *ksocknal_tunables.ksnd_rx_buffer_size);
741         if (rc != 0) {
742                 CERROR ("Can't set buffer tx %d, rx %d buffers: %d\n",
743                         *ksocknal_tunables.ksnd_tx_buffer_size,
744                         *ksocknal_tunables.ksnd_rx_buffer_size, rc);
745                 return (rc);
746         }
747
748 /* TCP_BACKOFF_* sockopt tunables unsupported in stock kernels */
749 #ifdef SOCKNAL_BACKOFF
750         if (*ksocknal_tunables.ksnd_backoff_init > 0) {
751                 option = *ksocknal_tunables.ksnd_backoff_init;
752
753                 set_fs (KERNEL_DS);
754                 rc = sock->ops->setsockopt (sock, SOL_TCP, TCP_BACKOFF_INIT,
755                                             (char *)&option, sizeof (option));
756                 set_fs (oldmm);
757                 if (rc != 0) {
758                         CERROR ("Can't set initial tcp backoff %d: %d\n",
759                                 option, rc);
760                         return (rc);
761                 }
762         }
763
764         if (*ksocknal_tunables.ksnd_backoff_max > 0) {
765                 option = *ksocknal_tunables.ksnd_backoff_max;
766
767                 set_fs (KERNEL_DS);
768                 rc = sock->ops->setsockopt (sock, SOL_TCP, TCP_BACKOFF_MAX,
769                                             (char *)&option, sizeof (option));
770                 set_fs (oldmm);
771                 if (rc != 0) {
772                         CERROR ("Can't set maximum tcp backoff %d: %d\n",
773                                 option, rc);
774                         return (rc);
775                 }
776         }
777 #endif
778
779         /* snapshot tunables */
780         keep_idle  = *ksocknal_tunables.ksnd_keepalive_idle;
781         keep_count = *ksocknal_tunables.ksnd_keepalive_count;
782         keep_intvl = *ksocknal_tunables.ksnd_keepalive_intvl;
783
784         do_keepalive = (keep_idle > 0 && keep_count > 0 && keep_intvl > 0);
785
786         option = (do_keepalive ? 1 : 0);
787         set_fs (KERNEL_DS);
788         rc = sock_setsockopt (sock, SOL_SOCKET, SO_KEEPALIVE,
789                               (char *)&option, sizeof (option));
790         set_fs (oldmm);
791         if (rc != 0) {
792                 CERROR ("Can't set SO_KEEPALIVE: %d\n", rc);
793                 return (rc);
794         }
795
796         if (!do_keepalive)
797                 return (0);
798
799         set_fs (KERNEL_DS);
800         rc = sock->ops->setsockopt (sock, SOL_TCP, TCP_KEEPIDLE,
801                                     (char *)&keep_idle, sizeof (keep_idle));
802         set_fs (oldmm);
803         if (rc != 0) {
804                 CERROR ("Can't set TCP_KEEPIDLE: %d\n", rc);
805                 return (rc);
806         }
807
808         set_fs (KERNEL_DS);
809         rc = sock->ops->setsockopt (sock, SOL_TCP, TCP_KEEPINTVL,
810                                     (char *)&keep_intvl, sizeof (keep_intvl));
811         set_fs (oldmm);
812         if (rc != 0) {
813                 CERROR ("Can't set TCP_KEEPINTVL: %d\n", rc);
814                 return (rc);
815         }
816
817         set_fs (KERNEL_DS);
818         rc = sock->ops->setsockopt (sock, SOL_TCP, TCP_KEEPCNT,
819                                     (char *)&keep_count, sizeof (keep_count));
820         set_fs (oldmm);
821         if (rc != 0) {
822                 CERROR ("Can't set TCP_KEEPCNT: %d\n", rc);
823                 return (rc);
824         }
825
826         return (0);
827 }
828
829 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
830 struct tcp_opt *sock2tcp_opt(struct sock *sk)
831 {
832         return &(sk->tp_pinfo.af_tcp);
833 }
834 #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10))
835 #define sock2tcp_opt(sk) tcp_sk(sk)
836 #else
837 struct tcp_opt *sock2tcp_opt(struct sock *sk)
838 {
839         struct tcp_sock *s = (struct tcp_sock *)sk;
840         return &s->tcp;
841 }
842 #endif
843
844 void
845 ksocknal_lib_push_conn (ksock_conn_t *conn)
846 {
847         struct sock    *sk;
848 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11))
849         struct tcp_opt *tp;
850 #else
851         struct tcp_sock *tp;
852 #endif
853         int             nonagle;
854         int             val = 1;
855         int             rc;
856         mm_segment_t    oldmm;
857
858         rc = ksocknal_connsock_addref(conn);
859         if (rc != 0)                            /* being shut down */
860                 return;
861
862         sk = conn->ksnc_sock->sk;
863         tp = sock2tcp_opt(sk);
864
865         lock_sock (sk);
866         nonagle = tp->nonagle;
867         tp->nonagle = 1;
868         release_sock (sk);
869
870         oldmm = get_fs ();
871         set_fs (KERNEL_DS);
872
873         rc = sk->sk_prot->setsockopt (sk, SOL_TCP, TCP_NODELAY,
874                                       (char *)&val, sizeof (val));
875         LASSERT (rc == 0);
876
877         set_fs (oldmm);
878
879         lock_sock (sk);
880         tp->nonagle = nonagle;
881         release_sock (sk);
882
883         ksocknal_connsock_decref(conn);
884 }
885
886 extern void ksocknal_read_callback (ksock_conn_t *conn);
887 extern void ksocknal_write_callback (ksock_conn_t *conn);
888 /*
889  * socket call back in Linux
890  */
891 static void
892 ksocknal_data_ready (struct sock *sk, int n)
893 {
894         ksock_conn_t  *conn;
895         ENTRY;
896
897         /* interleave correctly with closing sockets... */
898         LASSERT(!in_irq());
899         read_lock (&ksocknal_data.ksnd_global_lock);
900
901         conn = sk->sk_user_data;
902         if (conn == NULL) {             /* raced with ksocknal_terminate_conn */
903                 LASSERT (sk->sk_data_ready != &ksocknal_data_ready);
904                 sk->sk_data_ready (sk, n);
905         } else
906                 ksocknal_read_callback(conn);
907
908         read_unlock (&ksocknal_data.ksnd_global_lock);
909
910         EXIT;
911 }
912
913 static void
914 ksocknal_write_space (struct sock *sk)
915 {
916         ksock_conn_t  *conn;
917         int            wspace;
918         int            min_wpace;
919
920         /* interleave correctly with closing sockets... */
921         LASSERT(!in_irq());
922         read_lock (&ksocknal_data.ksnd_global_lock);
923
924         conn = sk->sk_user_data;
925         wspace = SOCKNAL_WSPACE(sk);
926         min_wpace = SOCKNAL_MIN_WSPACE(sk);
927
928         CDEBUG(D_NET, "sk %p wspace %d low water %d conn %p%s%s%s\n",
929                sk, wspace, min_wpace, conn,
930                (conn == NULL) ? "" : (conn->ksnc_tx_ready ?
931                                       " ready" : " blocked"),
932                (conn == NULL) ? "" : (conn->ksnc_tx_scheduled ?
933                                       " scheduled" : " idle"),
934                (conn == NULL) ? "" : (list_empty (&conn->ksnc_tx_queue) ?
935                                       " empty" : " queued"));
936
937         if (conn == NULL) {             /* raced with ksocknal_terminate_conn */
938                 LASSERT (sk->sk_write_space != &ksocknal_write_space);
939                 sk->sk_write_space (sk);
940
941                 read_unlock (&ksocknal_data.ksnd_global_lock);
942                 return;
943         }
944
945         if (wspace >= min_wpace) {              /* got enough space */
946                 ksocknal_write_callback(conn);
947
948                 /* Clear SOCK_NOSPACE _after_ ksocknal_write_callback so the
949                  * ENOMEM check in ksocknal_transmit is race-free (think about
950                  * it). */
951
952                 clear_bit (SOCK_NOSPACE, &sk->sk_socket->flags);
953         }
954
955         read_unlock (&ksocknal_data.ksnd_global_lock);
956 }
957
958 void
959 ksocknal_lib_save_callback(struct socket *sock, ksock_conn_t *conn)
960 {
961         conn->ksnc_saved_data_ready = sock->sk->sk_data_ready;
962         conn->ksnc_saved_write_space = sock->sk->sk_write_space;
963 }
964
965 void
966 ksocknal_lib_set_callback(struct socket *sock,  ksock_conn_t *conn)
967 {
968         sock->sk->sk_user_data = conn;
969         sock->sk->sk_data_ready = ksocknal_data_ready;
970         sock->sk->sk_write_space = ksocknal_write_space;
971         return;
972 }
973
974 void
975 ksocknal_lib_reset_callback(struct socket *sock, ksock_conn_t *conn)
976 {
977         /* Remove conn's network callbacks.
978          * NB I _have_ to restore the callback, rather than storing a noop,
979          * since the socket could survive past this module being unloaded!! */
980         sock->sk->sk_data_ready = conn->ksnc_saved_data_ready;
981         sock->sk->sk_write_space = conn->ksnc_saved_write_space;
982
983         /* A callback could be in progress already; they hold a read lock
984          * on ksnd_global_lock (to serialise with me) and NOOP if
985          * sk_user_data is NULL. */
986         sock->sk->sk_user_data = NULL;
987
988         return ;
989 }
990