a72e7eef7a2e981b5a80c3e0a3bb903ef7ee7367
[mirror/scst/.git] / www / contributing.html
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\r
2 <html>\r
3 <head>\r
4 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">\r
5 <meta name="author" content="Daniel Fernandes">\r
6 <meta name="Robots" content="index,follow">\r
7 <link rel="stylesheet" href="images/Orange.css" type="text/css">        \r
8 <title>SCST Contributing</title>\r
9 </head>\r
10 \r
11 <body>\r
12 <div id="wrap"> \r
13         <div id="header">       \r
14                 <div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>\r
15                 <h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>      \r
16         </div>\r
17         <div id="menu">\r
18                 <ul>\r
19                         <li><a href="index.html">Home</a></li>
20                         <li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>\r
21                         <li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
22                         <li><a href="targets.html">Drivers</a></li>\r
23                         <li><a href="downloads.html">Downloads</a></li>\r
24                         <li id="current"><a href="contributing.html">Contributing</a></li>\r
25                         <li><a href="comparison.html">Comparison</a></li>                       \r
26                         <li><a href="users.html">Users</a></li>
27                         <li><a href="solutions.html">Solutions</a></li>
28                 </ul>\r
29         </div>  \r
30         <div id="content-wrap">  \r
31                         <div id="main">\r
32                                 <h1>Contributing to SCST</h1>\r
33
34                                 <p>If you would like to contribute to SCST development, you can do in many ways:</p>
35
36                                 <ul>
37                                 <li><span>By sending donations. They will be spent on further work making SCST better as well as on providing
38                                 better support and troubleshooting for you. Donations can be on one time or per period of time basis,
39                                 from companies or individuals. They can be sent by direct bank transfer or using
40                                 Western Union, Moneygram or similar payment systems. Unfortunately, more convenient options,
41                                 like PayPal, are not available in Russia.
42                                 <li><span>By sending patches, which fix bugs or implement new functionality.
43                                           See below a list of possible SCST improvements with some possible 
44                                           implementation ideas.</span></li>
45                                 <li><span>By writing or updating various documentation to keep it complete and up to date.
46                                         For instance, <a href="scst_pg.html">SCST internals description</a> document is
47                                         in some areas quite outdated. Particularly, many functions were renamed since
48                                         time, when it was written. It would be good to bring it up to date.</span></li>
49                                 <li><span>By reporting bugs or other problems.</span></li>
50                                 </ul>                   \r
51
52                                 <p>Contributing in SCST, you investing not only
53                                 in making Linux <strong>the best storage OS</strong>, but, if your company
54                                 has a product or service, based on SCST, you also investing in <strong>the secured
55                                 future</strong> of your product or service and, hence, in the <strong>the secured
56                                 future</strong> of your company.</p>
57
58                                 <h1>Possible SCST extensions and improvements</h1>
59
60                                 <A NAME="ZC_READ"></A><h3>Zero-copy FILEIO for READ-direction commands</h3>
61
62                                 <p>At the moment, SCST in FILEIO mode uses standard Linux read() and write() syscalls paths,
63                                 which copy data from the page cache to the supplied buffer and back. Zero-copy FILEIO
64                                 would use page cache data directly. This would be a major performance improvement,
65                                 especially for fast hardware, like Infiniband, because it would eliminate the data copy
66                                 latency as well as considerably ease CPU and memory bandwidth load. This proposal is limited for
67                                 READs only, because for WRITEs it is a lot harder to
68                                 implement, so it is worth to do zero-copy for READs and WRITEs separately.</p>
69                                 
70                                 <p>The main idea is to add one more flag to filp_open() "flags" parameter
71                                 (like O_RDONLY, O_DIRECT, etc.) O_ZEROCOPY, which would be available 
72                                 only if the caller is from the kernel space. In this case fd->f_op->readv(), 
73                                 do_sync_readv_writev(), etc. would receive as the pointer to data 
74                                 buffer not a real data buffer, but pointer to an empty SG vector. Then:</p>
75                                 
76                                 <ul>
77                                 <li><span>Generic buffer allocation in SCST would not be used, instead vdisk_parse() 
78                                 would allocate the SG vector, but wouldn't fill it with actual pages.</span></li>
79                                 
80                                 <li><span>In generic_file_aio_read(), if O_ZEROCOPY flag was set,
81                                 function do_generic_file_read() would be called with the last parameter set
82                                 to a pointer to new function file_zero_copy_read_actor() instead of file_read_actor().</span></li>
83                                 
84                                 <li><span>Function file_zero_copy_read_actor() would be basically the same as
85                                 file_read_actor(), but, instead of copy data using __copy_to_user*() functions,
86                                 it would add the supplied page to the appropriate place in the received in
87                                 desc->arg.buf SG vector and reference, i.e. page_get(), that page.</span></li>
88                                 
89                                 <li><span>In vdisk_devtype.on_free_cmd(), which doesn't exist yet, all pages 
90                                 from the SG vector would be dereferenced, i.e. page_put(). Then the SG vector itself 
91                                 would be freed.</span></li>
92                                 </ul>
93                                 
94                                 <p>That's all. For WRITEs the current code path would remain unchanged.</p>
95
96                                 <A NAME="ZC_WRITE"></A><h3>Zero-copy FILEIO for WRITE-direction commands</h3>
97
98                                 <p>Implementation should be similar to zero-copy FILEIO for READ commands and should
99                                 be done after it. All incoming data should be inserted in the page cache, then dereferenced in
100                                 vdisk_devtype.on_free_cmd(). The main problem is insertion of data pages in the
101                                 page cache, namely, locking issues related to it. They should be carefully
102                                 investigated.</p>
103
104                                 <A NAME="PR"></A><h3>Persistent reservations</h3>
105
106                                 <p>Support for PERSISTENT RESERVE IN and PERSISTENT RESERVE OUT is required to
107                                 work in many cluster environments, e.g. Windows 2003 Cluster.</p>
108                                 
109                                 <p>For implementation you should use scst_reserve_local() and 
110                                 scst_release_local() as a base. You should store all reservation keys 
111                                 for in files in /var/scst, one file per device 
112                                 (it would allow to eliminate additional locking), like 
113                                 /var/scst/boot_disk for device "boot_disk" and load them in memory, when 
114                                 device would be registered.</p>
115                                 
116                                 <p>In the first version it can be done for virtual 
117                                 devices only and reject PERSISTENT RESERVE IN and OUT commands for 
118                                 pass-through devices with "COMMAND NOT SUPPORTED" sense data.</p>
119                                 
120                                 <A NAME="DYN_FLOW"></A><h3>Dynamic I/O flow control</h3>
121                                 
122                                 <p>At the moment, if an initiator or several initiators simultaneously send to
123                                 target too many commands, especially in seek intensive workloads, target can get
124                                 overloaded and not able to finish commands on time. In such cases you can see on
125                                 the initiator(s) messages about aborting commands or resetting the target. See in SCST core
126                                 README section "What if target's backstorage is too slow" for more details.
127                                 To fix this problem it is necessary to implement a dynamic I/O flow control in
128                                 SCST core.</p>
129                                 
130                                 <p>The flow control, generally, is quite simple. Each SCST command has timeout value,
131                                 which is set by the corresponding dev handler. SCST core should keep device's queue depth
132                                 at the level that the worst command's execution time, i.e. time between scst_rx_cmd()
133                                 and scst_finish_cmd(), would be between something like timeout/10 and timeout/5.
134                                 So, commands execution time should be checked and:</p>
135                                 
136                                 <ul>
137                                 <li><span>If it's > timeout/5, then the new queue depth should be set to max(1, 
138                                   cur_depth/2)</span></li>
139                                   
140                                 <li><span>If it's < timeout/10, then new queue depth should be set to min(MAX_DEPTH, 
141                                     cur_depth+1). This shouldn't be done too often, once in a few minutes should be
142                                     sufficient</span></li>
143                                 </ul>    
144
145                                 <p>The above is, of course, an oversimplification to let you see the idea. 
146                                 Implementation considering real life cases should be as the following:</p>
147
148                                 <p>1. There are several parameters:</p>
149                                 
150                                 <ul>
151                                 <li><span>P - load watch period. During this period all the statistic is 
152                                 gathered and processed.</span></li>
153
154                                 <li><span>MN - underload ratio divisor, which sets the underload portion of 
155                                 timeout. If the longest execution time among all commands completed 
156                                 during period P is below timeout/MN, the corresponding device considered 
157                                 underloaded.</span></li>
158
159                                 <li><span>MX - overload ratio divisor, which sets the overload portion of 
160                                 timeout. If the longest execution time among all commands completed 
161                                 during period P is above timeout/MX, the corresponding device considered 
162                                 overloaded.</span></li>
163
164                                 <li><span>I - step on which device's queue size will be increased if device 
165                                 considered underloaded.</span></li>
166
167                                 <li><span>D - divisor on which device's queue size will be decreased if device 
168                                 considered overloaded.</span></li>
169
170                                 <li><span>QI - quick fall interval. See description of Q parameter.</span></li>
171
172                                 <li><span>Q - quick fall ratio divisor. If the longest execution time of a 
173                                 completed command is above timeout/Q and time from the previous quick 
174                                 fall is smaller than QI, the corresponding device considered heavily 
175                                 overloaded. The quick fall is needed to handle cases when load on device 
176                                 is instantly increased on the way, where it can't handle it properly.</span></li>
177
178                                 <li><span>QD  - divisor on which device's queue size will be decreased if 
179                                 device considered heavily overloaded.</span></li>
180                                 </ul>
181
182                                 <p>The default values should be something like: P=15 sec., MN=20, MX=10, Q=3, 
183                                 I=1, D=2, QI=5 sec., QD=10.</p>
184
185                                 <p>2. There are the following new variables in struct scst_device:</p>
186
187                                 <ul>
188                                 <li><span>queue_depth - current queue depth.</span></li>
189
190                                 <li><span>max_exec_ratio - maximum commands timeout/(execution time).</span></li>
191
192                                 <li><span>queue_was_full - flag, marking that the queue was at least once full 
193                                 during period P.</span></li>
194
195                                 <li><span>quick_fall_time - time of the last quick fall.</span></li>
196
197                                 <li><span>flow_lock - protects flow control related variables, where needed.</span></li>
198                                   
199                                 <li><span>...</span></li>
200                                 </ul>
201
202                                 <p>3. The commands processing path should be as the following:</p>
203
204                                 <ul>
205                                 <li><span>In scst_rx_cmd() the start time of the command is recorded (already done).</span></li>
206
207                                 <li><span>In __scst_init_cmd(), if dev->dev_cmd_count == dev->queue_depth, 
208                                 dev->queue_was_full set to true.</span></li>
209
210                                 <li><span>In scst_finish_cmd() dev->max_exec_ratio set to max(dev->max_exec_ratio, 
211                                 (cmd's exec_time)*100/cmd->timeout).</span></li>
212
213                                 <li><span>If in scst_finish_cmd() cmd's exec time is above cmd->timeout/Q and 
214                                 time from the latest quick fall is above QI, then:
215
216                                         <ul>
217                                         <li><span>dev->queue_depth set to max(1, dev->queue_depth/QD).</span></li>
218                                 
219                                         <li><span>Flow control period reset, i.e. started again, including setting 
220                                         dev->max_exec_ratio to 0 and dev->quick_fall_time to jiffies.</span></li>
221                                         </ul>
222                                         </span></li>
223                                 </ul>
224
225                                 <p>4. There should be a work, which once in a P seconds will check 
226                                 dev->max_exec_ratio, then:</p>
227
228                                 <ul>
229                                 <li><span>If device neither underloaded, nor overloaded. i.e. max_exec_ratio 
230                                 between defined by MN and MX, do nothing.</span></li>
231
232                                 <li><span>If device was underloaded:
233                                 
234                                         <ul>
235                                         <li><span>if dev->queue_was_full is false, then do nothing.</span></li>
236
237                                         <li><span>if dev->queue_was_full is true, then set dev->queue_depth to
238                                         min(SCST_MAX_DEV_COMMANDS, dev->queue_depth + I).</span></li>
239                                         </ul>
240                                         </span></li>
241
242                                 <li><span>If device was overloaded, then set dev->queue_depth to max(1, 
243                                 dev->queue_depth/D).</span></li>
244                                 </ul>
245
246                                 <p>Then the flow control period is reset, i.e. started again, including 
247                                 setting dev->max_exec_ratio to 0 and dev->quick_fall_time to jiffies.</p>
248
249                                 <p>That's all. Then only support for initiators, like iSCSI, 
250                                 which don't handle QUEUE FULL to decrease amount of queued 
251                                 commands, should be added. Such initiators expect target to control size of
252                                 the queue, via, e.g., through MAX_SN for iSCSI.</p>
253
254                                 <p>For it at the stage 2 of the dynamic flow control development 
255                                 the following should be done:</p>
256
257                                 <ul>
258                                 <li><span>New callback on_queue_depth_adjustment() should be added to struct 
259                                 scst_tgt_template.</span></li>
260
261                                 <li><span>If target driver defined it, each time after dev->queue_depth changed 
262                                 on_queue_depth_adjustment() should be called. In this callback target 
263                                 driver should change internal queue_depth to, e.g. for iSCSI target, set 
264                                 max_sn in the replies correctly.</span></li>
265                                 </ul>
266
267                                 <p>Then, at the latest stage of the development, logic to not schedule the 
268                                 flow control work on idle devices should be added.</p>
269
270                                 <A NAME="O_DIRECT"></A><h3>Support for O_DIRECT in scst_vdisk handler</h3>
271
272                                 <p>At the moment, scst_vdisk handler doesn't support O_DIRECT option and possibility to set it
273                                 was disabled. This limitation caused by Linux kernel expectation that memory supplied to
274                                 read() and write() functions with O_DIRECT flag is mapped to some user space application.</p>
275                                 
276                                 <p>It is relatively easy to remove that limitation. Function dio_refill_pages()
277                                 should be modified to check before calling get_user_pages() if current->mm is not NULL.
278                                 If it is NULL, then, instead of calling get_user_pages(), dio->pages should be filled
279                                 by pages, taken directly from dio->curr_user_address. Each such page should be referenced
280                                 by page_cache_get(). That's all.</p>
281
282                                 <A NAME="VDISK_REFACTOR"></A><h3>Refactoring of command execution path in scst_vdisk handler</h3>
283                                 
284                                 <p>At the moment, in scst_vdisk handler command execution function vdisk_do_job() is
285                                 overcomplicated and not very performance effective. It would be good to replace all those
286                                 ugly "switch" statements by choosing the handler for each SCSI command by indirect
287                                 function call on an array of function pointers.</p>
288                                 
289                                 <p>I.e., there should be an array vdisk_exec_fns with 256 entries of function pointers:</p>
290
291                                  <p>void (*cmd_exec_fn) (struct scst_cmd *cmd)</p>
292                                  
293                                  <p>Then vdisk_do_job() should look like</p>
294                                  
295                                  <listing><p>static int vdisk_do_job(struct scst_cmd *cmd)
296 {
297         return vdisk_exec_fns[cmd->cdb[0]](cmd);
298 }</p></listing>
299
300                                 <A NAME="SG_LIMIT"></A><h3>Solve SG IO count limitation issue in pass-through mode</h3>
301                                 
302                                 <p>In the pass-through mode (i.e. using the pass-through device handlers
303                                 scst_disk, scst_tape, etc) SCSI commands, coming from remote initiators,
304                                 are passed to local SCSI hardware on target as is, without any
305                                 modifications. As any other hardware, the local SCSI hardware can not
306                                 handle commands with amount of data and/or segments count in
307                                 scatter-gather array bigger some values. If you have this issue you will see
308                                 symptoms like small transfers work well, but large ones stall and
309                                 messages like: "Unable to complete command due to SG IO count
310                                 limitation" are printed in the kernel logs.</p>
311                                 
312                                 <p>In <a href="sgv_big_order_alloc.diff">sgv_big_order_alloc.diff</a> you
313                                 can find a possible way to solve this issue.</p>
314
315                                 <p>There are also 2 more patches you can look at:</p>
316                                 
317                                 <ul>
318                                 
319                                 <li><span><a href="sgv_big_order_alloc-r2.diff">sgv_big_order_alloc-r2.diff</a> - this patch
320                                 has all the required features, but has a memory corruption.</li></span>
321
322                                 <li><span><a href="sgv_big_order_alloc-sfw4.diff">sgv_big_order_alloc-sfw4.diff</a> - this patch,
323                                 created by Frank Zago, works for him, but doesn't have all the required features to be merged
324                                 in SCST.</li></span>
325
326                                 </ul>
327
328                                 <A NAME="MEM_REG"></A><h3>Memory registration</h3>
329                                 
330                                 <p>In some cases a target driver might need to register memory used for data buffers in the
331                                 hardware. At the moment, none of SCST target drivers, including InfiniBand SRP target driver,
332                                 need that feature. But in case if in future there is a need in such a feature, it can be easily
333                                 added by extending SCST SGV cache. The SCST SGV cache is a memory management 
334                                 subsystem in SCST. It doesn't free to the system each data buffer,
335                                 which is not used anymore, but keeps it for a while to let it be reused by the 
336                                 next consecutive command to reduce command processing latency and, hence, improve performance.</p>
337                                 
338                                 <p>To support memory buffers registrations, it can be extended by the following way:</p>
339                                 
340                                 <p>1. Struct scst_tgt_template would be extended to have 2 new callbacks:</p>
341                                 
342                                 <ul>
343
344                                         <li><span>int register_buffer(struct scst_cmd *cmd)</span></li>
345                                         
346                                         <li><span>int unregister_buffer(unsigned long mem_priv, void *scst_priv)</span></li>
347                         
348                                 </ul>
349                                 
350                                 <p>2. SCST core would be extended to have 4 new functions:</p>
351
352                                 <ul>
353
354                                         <li><span>int scst_mem_registered(struct scst_cmd *cmd)</span></li>
355                                         
356                                         <li><span>int scst_mem_deregistered(void *scst_priv)</span></li>
357                                         
358                                         <li><span>int scst_set_mem_priv(struct scst_cmd *cmd, unsigned long mem_priv)</span></li>
359                                         
360                                         <li><span>unsigned long scst_get_mem_priv(struct scst_cmd *cmd)</span></li>
361                         
362                                 </ul>
363                                 
364                                 <p>3. The workflow would be the following:</p>
365                                 
366                                 <ol>
367                                         <li><span>If target driver defined register_buffer() and unregister_buffer() callbacks,
368                                         SCST core would allocate a dedicated SGV cache for each instance of struct scst_tgt,
369                                         i.e. target.</span></li>
370
371                                         <li><span>When there would be an SGV cache miss in memory buffer for a command allocation,
372                                         SCST would check if register_buffer() callback was defined in the target driver's template
373                                         and, if yes, would call it.</span></li>
374                                         
375                                         <li><span>In register_buffer() callback the target driver would do necessary actions to
376                                         start registration of the commands memory buffer.</span></li>
377                                         
378                                         <li><span>Upon register_buffer() callback returns, SCST core would suspend processing the
379                                         corresponding command and would switch to the next commands processing.</span></li>
380                                         
381                                         <li><span>After the memory registration finished, the target driver would call scst_set_mem_priv()
382                                         to associate the memory buffer with some internal data.</span></li>
383                                         
384                                         <li><span>Then the target driver would call scst_mem_registered() and SCST would resume processing
385                                         the command. Functions scst_set_mem_priv() and scst_mem_registered() can be called from inside register_buffer().
386                                         In this case SCST core would continue processing the command immediately without suspending.</span></li>
387                                         
388                                         <li><span>After the command finished, the corresponding memory buffer would remain in the
389                                         SGV cache in the registered state and would be reused by the next commands. For each of them
390                                         the target driver can at any time figure out the associated with the registered buffer data
391                                         by using scst_get_mem_priv().</span></li>
392                                         
393                                         <li><span>When the SGV cache decide that there is a time to free the memory buffer, it would
394                                         call the target driver's unregister_buffer() callback.</span></li>
395                         
396                                         <li><span>In this callback the target driver would do necessary actions to start deregistration of the
397                                         commands memory buffer.</span></li>
398                                         
399                                         <li><span>Upon unregister_buffer() callback returns, SGV cache would suspend freeing the corresponding buffer
400                                         and would switch to other deals it has.</span></li>
401
402                                         <li><span>After the memory deregistration finished, the target driver would call scst_mem_deregistered()
403                                         and pass to it scst_priv pointer, received in unregister_buffer(). Then the  memory buffer
404                                         would be freed by the SGV cache. Function scst_mem_deregistered() can be called from inside unregister_buffer().
405                                         In this case SGV cache would free the buffer immediately without suspending.
406                                         </span></li>
407                                 </ol>
408
409                                 <A NAME="NON_SCSI_TGT"></A><h3>SCST usage with non-SCSI transports</h3>
410                                 
411                                 <p>SCST might also be used with non-SCSI speaking transports, like NBD or AoE. Such cooperation
412                                 would allow them to use SCST-emulated backend.</p>
413                                 
414                                 <p>For user space targets this is trivial: they simply should use SCST-emulated devices locally
415                                 via scst_local module.</p>
416                                 
417                                 <p>For in-kernel non-SCSI target driver it's a bit more complicated. They should implement a small layer,
418                                 which would translate their internal READ/WRITE requests to corresponding SCSI commands and, on the
419                                 way back, SCSI status and sense codes to their internal status codes.</p>
420
421                                 <A NAME="iSER_target"></A><h3>iSER target</h3>
422
423                                 <p><a href="http://en.wikipedia.org/wiki/ISCSI_Extensions_for_RDMA">iSER</a> (iSCSI Extensions for RDMA)
424                                 protocol accelerates iSCSI by allowing direct data transfers using RDMA services (iWARP or
425                                 InfiniBand) bypassing the regular heavy weighted and CPU consuming TCP/IP data transfers path.</p>
426
427                                 <p>It would be good to add support for iSER in iSCSI-SCST.</p>
428
429                                 <A NAME="GET_CONFIGURATION"></A><h3>GET CONFIGURATION command</h3>
430
431                                 <p>SCSI command GET CONFIGURATION is mandatory for SCSI multimedia devices, like CD/DVD-ROMs or
432                                 recorders, see MMC standard. Currently SCST lacks support for it, which leads to problems
433                                 with some programs depending on the result of GET CONFIGURATION command execution.</p>
434
435                                 <p>It would be good to add support for it in the SCST core.</p>
436
437                                 <A NAME="Per_Device_Suspend"></A><h3>Per-device suspending</h3>
438
439                                 <p>Currently before doing any management operations SCST core performs so called "activities suspending", i.e.
440                                 it suspends new coming SCSI commands and wait until currently being executed ones finished. It allows to
441                                 simplify internal locking and reference counting a lot, but has a drawback that it is global, i.e. affects
442                                 all devices and SCSI commands, even ones which don't participate in the management operation. In the majority
443                                 of regular cases it works pretty well, but sometimes it can be a problem.
444                                 For instance, if a SCSI command needs a big amount of execution time (hours for some tapes operations),
445                                 the management command and all other SCSI commands will wait until it's finished. Even worse, if a user space
446                                 dev handler hangs and stops processing commands, any SCST management command will not be able to complete and fail
447                                 with timeout until the user space dev handler gets killed.</p>
448
449                                 <p>The global suspending should be changed to more fine-grained per-device suspending
450                                 and only for cases where it's really needed, like device unregistration. This is a very tricky task, because
451                                 all the internal SCST locking should be reimplemented.</p>
452
453                         </div>
454         </div>
455 </div>
456 <!-- wrap ends here -->\r\r
457 <!-- footer starts here -->             \r
458                 <div id="footer">\r
459                         <p>\r
460                         &copy; Copyright 2004-2009 <b><font color="#EC981F">Vladislav Bolkhovitin &amp others.</font>&nbsp;&nbsp;\r
461                         Design by: <b><font color="#EC981F">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;         \r
462                         \r
463                         </p>            \r
464                 </div>  \r
465 <!-- footer ends here -->\r
466 </body> 
467
468 <!-- Piwik -->
469 <script type="text/javascript">
470 var pkBaseURL = (("https:" == document.location.protocol) ? "https://apps.sourceforge.net/piwik/scst/" : "http://apps.sourceforge.net/piwik/scst/");
471 document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
472 </script><script type="text/javascript">
473 piwik_action_name = '';
474 piwik_idsite = 1;
475 piwik_url = pkBaseURL + "piwik.php";
476 piwik_log(piwik_action_name, piwik_idsite, piwik_url);
477 </script>
478 <object><noscript><p><img src="http://apps.sourceforge.net/piwik/scst/piwik.php?idsite=1" alt="piwik"/></p></noscript></object>
479 <!-- End Piwik Tag -->
480
481 </html>