[DLM] Clean up lowcomms
[pandora-kernel.git] / drivers / char / ftape / zftape / zftape-write.c
1 /*
2  *      Copyright (C) 1996, 1997 Claus Heine
3
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2, or (at your option)
7  any later version.
8
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  GNU General Public License for more details.
13
14  You should have received a copy of the GNU General Public License
15  along with this program; see the file COPYING.  If not, write to
16  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
17
18  *
19  * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-write.c,v $
20  * $Revision: 1.3 $
21  * $Date: 1997/11/06 00:50:29 $
22  *
23  *      This file contains the writing code
24  *      for the QIC-117 floppy-tape driver for Linux.
25  */
26
27 #include <linux/errno.h>
28 #include <linux/mm.h>
29
30 #include <linux/zftape.h>
31
32 #include <asm/uaccess.h>
33
34 #include "../zftape/zftape-init.h"
35 #include "../zftape/zftape-eof.h"
36 #include "../zftape/zftape-ctl.h"
37 #include "../zftape/zftape-write.h"
38 #include "../zftape/zftape-read.h"
39 #include "../zftape/zftape-rw.h"
40 #include "../zftape/zftape-vtbl.h"
41
42 /*      Global vars.
43  */
44
45 /*      Local vars.
46  */
47 static int last_write_failed;
48 static int need_flush;
49
50 void zft_prevent_flush(void)
51 {
52         need_flush = 0;
53 }
54
55 static int zft_write_header_segments(__u8* buffer)
56 {
57         int header_1_ok = 0;
58         int header_2_ok = 0;
59         unsigned int time_stamp;
60         TRACE_FUN(ft_t_noise);
61         
62         TRACE_CATCH(ftape_abort_operation(),);
63         ftape_seek_to_bot();    /* prevents extra rewind */
64         if (GET4(buffer, 0) != FT_HSEG_MAGIC) {
65                 TRACE_ABORT(-EIO, ft_t_err,
66                             "wrong header signature found, aborting");
67         } 
68         /*   Be optimistic: */
69         PUT4(buffer, FT_SEG_CNT,
70              zft_written_segments + GET4(buffer, FT_SEG_CNT) + 2);
71         if ((time_stamp = zft_get_time()) != 0) {
72                 PUT4(buffer, FT_WR_DATE, time_stamp);
73                 if (zft_label_changed) {
74                         PUT4(buffer, FT_LABEL_DATE, time_stamp);
75                 }
76         }
77         TRACE(ft_t_noise,
78               "writing first header segment %d", ft_header_segment_1);
79         header_1_ok = zft_verify_write_segments(ft_header_segment_1, 
80                                                 buffer, FT_SEGMENT_SIZE,
81                                                 zft_deblock_buf) >= 0;
82         TRACE(ft_t_noise,
83               "writing second header segment %d", ft_header_segment_2);
84         header_2_ok = zft_verify_write_segments(ft_header_segment_2, 
85                                                 buffer, FT_SEGMENT_SIZE,
86                                                 zft_deblock_buf) >= 0;
87         if (!header_1_ok) {
88                 TRACE(ft_t_warn, "Warning: "
89                       "update of first header segment failed");
90         }
91         if (!header_2_ok) {
92                 TRACE(ft_t_warn, "Warning: "
93                       "update of second header segment failed");
94         }
95         if (!header_1_ok && !header_2_ok) {
96                 TRACE_ABORT(-EIO, ft_t_err, "Error: "
97                       "update of both header segments failed.");
98         }
99         TRACE_EXIT 0;
100 }
101
102 int zft_update_header_segments(void)
103 {
104         TRACE_FUN(ft_t_noise);
105         
106         /*  must NOT use zft_write_protected, as it also includes the
107          *  file access mode. But we also want to update when soft
108          *  write protection is enabled (O_RDONLY)
109          */
110         if (ft_write_protected || zft_old_ftape) {
111                 TRACE_ABORT(0, ft_t_noise, "Tape set read-only: no update");
112         } 
113         if (!zft_header_read) {
114                 TRACE_ABORT(0, ft_t_noise, "Nothing to update");
115         }
116         if (!zft_header_changed) {
117                 zft_header_changed = zft_written_segments > 0;
118         }
119         if (!zft_header_changed && !zft_volume_table_changed) {
120                 TRACE_ABORT(0, ft_t_noise, "Nothing to update");
121         }
122         TRACE(ft_t_noise, "Updating header segments");
123         if (ftape_get_status()->fti_state == writing) {
124                 TRACE_CATCH(ftape_loop_until_writes_done(),);
125         }
126         TRACE_CATCH(ftape_abort_operation(),);
127         
128         zft_deblock_segment = -1; /* invalidate the cache */
129         if (zft_header_changed) {
130                 TRACE_CATCH(zft_write_header_segments(zft_hseg_buf),);
131         }
132         if (zft_volume_table_changed) {
133                 TRACE_CATCH(zft_update_volume_table(ft_first_data_segment),);
134         }
135         zft_header_changed =
136                 zft_volume_table_changed = 
137                 zft_label_changed        =
138                 zft_written_segments     = 0;
139         TRACE_CATCH(ftape_abort_operation(),);
140         ftape_seek_to_bot();
141         TRACE_EXIT 0;
142 }
143
144 static int read_merge_buffer(int seg_pos, __u8 *buffer, int offset, int seg_sz)
145 {
146         int result = 0;
147         const ft_trace_t old_tracing = TRACE_LEVEL;
148         TRACE_FUN(ft_t_flow);
149         
150         if (zft_qic_mode) {
151                 /*  writing in the middle of a volume is NOT allowed
152                  *
153                  */
154                 TRACE(ft_t_noise, "No need to read a segment");
155                 memset(buffer + offset, 0, seg_sz - offset);
156                 TRACE_EXIT 0;
157         }
158         TRACE(ft_t_any, "waiting");
159         ftape_start_writing(FT_WR_MULTI);
160         TRACE_CATCH(ftape_loop_until_writes_done(),);
161         
162         TRACE(ft_t_noise, "trying to read segment %d from offset %d",
163               seg_pos, offset);
164         SET_TRACE_LEVEL(ft_t_bug);
165         result = zft_fetch_segment_fraction(seg_pos, buffer, 
166                                             FT_RD_SINGLE,
167                                             offset, seg_sz - offset);
168         SET_TRACE_LEVEL(old_tracing);
169         if (result != (seg_sz - offset)) {
170                 TRACE(ft_t_noise, "Ignore error: read_segment() result: %d",
171                       result);
172                 memset(buffer + offset, 0, seg_sz - offset);
173         }
174         TRACE_EXIT 0;
175 }
176
177 /* flush the write buffer to tape and write an eof-marker at the
178  * current position if not in raw mode.  This function always
179  * positions the tape before the eof-marker.  _ftape_close() should
180  * then advance to the next segment.
181  *
182  * the parameter "finish_volume" describes whether to position before
183  * or after the possibly created file-mark. We always position after
184  * the file-mark when called from ftape_close() and a flush was needed
185  * (that is ftape_write() was the last tape operation before calling
186  * ftape_flush) But we always position before the file-mark when this
187  * function get's called from outside ftape_close() 
188  */
189 int zft_flush_buffers(void)
190 {
191         int result;
192         int data_remaining;
193         int this_segs_size;
194         TRACE_FUN(ft_t_flow);
195
196         TRACE(ft_t_data_flow,
197               "entered, ftape_state = %d", ftape_get_status()->fti_state);
198         if (ftape_get_status()->fti_state != writing && !need_flush) {
199                 TRACE_ABORT(0, ft_t_noise, "no need for flush");
200         }
201         zft_io_state = zft_idle; /*  triggers some initializations for the
202                                   *  read and write routines 
203                                   */
204         if (last_write_failed) {
205                 ftape_abort_operation();
206                 TRACE_EXIT -EIO;
207         }
208         TRACE(ft_t_noise, "flushing write buffers");
209         this_segs_size = zft_get_seg_sz(zft_pos.seg_pos);
210         if (this_segs_size == zft_pos.seg_byte_pos) {
211                 zft_pos.seg_pos ++;
212                 data_remaining = zft_pos.seg_byte_pos = 0;
213         } else {
214                 data_remaining = zft_pos.seg_byte_pos;
215         }
216         /* If there is any data not written to tape yet, append zero's
217          * up to the end of the sector (if using compression) or merge
218          * it with the data existing on the tape Then write the
219          * segment(s) to tape.
220          */
221         TRACE(ft_t_noise, "Position:\n"
222               KERN_INFO "seg_pos  : %d\n"
223               KERN_INFO "byte pos : %d\n"
224               KERN_INFO "remaining: %d",
225               zft_pos.seg_pos, zft_pos.seg_byte_pos, data_remaining);
226         if (data_remaining > 0) {
227                 do {
228                         this_segs_size = zft_get_seg_sz(zft_pos.seg_pos);
229                         if (this_segs_size > data_remaining) {
230                                 TRACE_CATCH(read_merge_buffer(zft_pos.seg_pos,
231                                                               zft_deblock_buf,
232                                                               data_remaining,
233                                                               this_segs_size),
234                                             last_write_failed = 1);
235                         }
236                         result = ftape_write_segment(zft_pos.seg_pos, 
237                                                      zft_deblock_buf,
238                                                      FT_WR_MULTI);
239                         if (result != this_segs_size) {
240                                 TRACE(ft_t_err, "flush buffers failed");
241                                 zft_pos.tape_pos    -= zft_pos.seg_byte_pos;
242                                 zft_pos.seg_byte_pos = 0;
243
244                                 last_write_failed = 1;
245                                 TRACE_EXIT result;
246                         }
247                         zft_written_segments ++;
248                         TRACE(ft_t_data_flow,
249                               "flush, moved out buffer: %d", result);
250                         /* need next segment for more data (empty segments?)
251                          */
252                         if (result < data_remaining) { 
253                                 if (result > 0) {       
254                                         /* move remainder to buffer beginning 
255                                          */
256                                         memmove(zft_deblock_buf, 
257                                                 zft_deblock_buf + result,
258                                                 FT_SEGMENT_SIZE - result);
259                                 }
260                         } 
261                         data_remaining -= result;
262                         zft_pos.seg_pos ++;
263                 } while (data_remaining > 0);
264                 TRACE(ft_t_any, "result: %d", result);
265                 zft_deblock_segment = --zft_pos.seg_pos;
266                 if (data_remaining == 0) {  /* first byte next segment */
267                         zft_pos.seg_byte_pos = this_segs_size;
268                 } else { /* after data previous segment, data_remaining < 0 */
269                         zft_pos.seg_byte_pos = data_remaining + result;
270                 }
271         } else {
272                 TRACE(ft_t_noise, "zft_deblock_buf empty");
273                 zft_pos.seg_pos --;
274                 zft_pos.seg_byte_pos = zft_get_seg_sz (zft_pos.seg_pos);
275                 ftape_start_writing(FT_WR_MULTI);
276         }
277         TRACE(ft_t_any, "waiting");
278         if ((result = ftape_loop_until_writes_done()) < 0) {
279                 /* that's really bad. What to to with zft_tape_pos? 
280                  */
281                 TRACE(ft_t_err, "flush buffers failed");
282         }
283         TRACE(ft_t_any, "zft_seg_pos: %d, zft_seg_byte_pos: %d",
284               zft_pos.seg_pos, zft_pos.seg_byte_pos);
285         last_write_failed  =
286                 need_flush = 0;
287         TRACE_EXIT result;
288 }
289
290 /* return-value: the number of bytes removed from the user-buffer
291  *
292  * out: 
293  *      int *write_cnt: how much actually has been moved to the
294  *                      zft_deblock_buf
295  *      int req_len  : MUST NOT BE CHANGED, except at EOT, in 
296  *                      which case it may be adjusted
297  * in : 
298  *      char *buff        : the user buffer
299  *      int buf_pos_write : copy of buf_len_wr int
300  *      this_segs_size    : the size in bytes of the actual segment
301  *                          char
302  *      *zft_deblock_buf   : zft_deblock_buf
303  */
304 static int zft_simple_write(int *cnt,
305                             __u8 *dst_buf, const int seg_sz,
306                             const __u8 __user *src_buf, const int req_len, 
307                             const zft_position *pos,const zft_volinfo *volume)
308 {
309         int space_left;
310         TRACE_FUN(ft_t_flow);
311
312         /* volume->size holds the tape capacity while volume is open */
313         if (pos->tape_pos + volume->blk_sz > volume->size) {
314                 TRACE_EXIT -ENOSPC;
315         }
316         /*  remaining space in this segment, NOT zft_deblock_buf
317          */
318         space_left = seg_sz - pos->seg_byte_pos;
319         *cnt = req_len < space_left ? req_len : space_left;
320         if (copy_from_user(dst_buf + pos->seg_byte_pos, src_buf, *cnt) != 0) {
321                 TRACE_EXIT -EFAULT;
322         }
323         TRACE_EXIT *cnt;
324 }
325
326 static int check_write_access(int req_len,
327                               const zft_volinfo **volume,
328                               zft_position *pos,
329                               const unsigned int blk_sz)
330 {
331         int result;
332         TRACE_FUN(ft_t_flow);
333
334         if ((req_len % zft_blk_sz) != 0) {
335                 TRACE_ABORT(-EINVAL, ft_t_info,
336                             "write-count %d must be multiple of block-size %d",
337                             req_len, blk_sz);
338         }
339         if (zft_io_state == zft_writing) {
340                 /*  all other error conditions have been checked earlier
341                  */
342                 TRACE_EXIT 0;
343         }
344         zft_io_state = zft_idle;
345         TRACE_CATCH(zft_check_write_access(pos),);
346         /*  If we haven't read the header segment yet, do it now.
347          *  This will verify the configuration, get the bad sector
348          *  table and read the volume table segment 
349          */
350         if (!zft_header_read) {
351                 TRACE_CATCH(zft_read_header_segments(),);
352         }
353         /*  fine. Now the tape is either at BOT or at EOD,
354          *  Write start of volume now
355          */
356         TRACE_CATCH(zft_open_volume(pos, blk_sz, zft_use_compression),);
357         *volume = zft_find_volume(pos->seg_pos);
358         DUMP_VOLINFO(ft_t_noise, "", *volume);
359         zft_just_before_eof = 0;
360         /* now merge with old data if necessary */
361         if (!zft_qic_mode && pos->seg_byte_pos != 0){
362                 result = zft_fetch_segment(pos->seg_pos,
363                                            zft_deblock_buf,
364                                            FT_RD_SINGLE);
365                 if (result < 0) {
366                         if (result == -EINTR || result == -ENOSPC) {
367                                 TRACE_EXIT result;
368                         }
369                         TRACE(ft_t_noise, 
370                               "ftape_read_segment() result: %d. "
371                               "This might be normal when using "
372                               "a newly\nformatted tape", result);
373                         memset(zft_deblock_buf, '\0', pos->seg_byte_pos);
374                 }
375         }
376         zft_io_state = zft_writing;
377         TRACE_EXIT 0;
378 }
379
380 static int fill_deblock_buf(__u8 *dst_buf, const int seg_sz,
381                             zft_position *pos, const zft_volinfo *volume,
382                             const char __user *usr_buf, const int req_len)
383 {
384         int cnt = 0;
385         int result = 0;
386         TRACE_FUN(ft_t_flow);
387
388         if (seg_sz == 0) {
389                 TRACE_ABORT(0, ft_t_data_flow, "empty segment");
390         }
391         TRACE(ft_t_data_flow, "\n"
392               KERN_INFO "remaining req_len: %d\n"
393               KERN_INFO "          buf_pos: %d", 
394               req_len, pos->seg_byte_pos);
395         /* zft_deblock_buf will not contain a valid segment any longer */
396         zft_deblock_segment = -1;
397         if (zft_use_compression) {
398                 TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),);
399                 TRACE_CATCH(result= (*zft_cmpr_ops->write)(&cnt,
400                                                            dst_buf, seg_sz,
401                                                            usr_buf, req_len,
402                                                            pos, volume),);
403         } else {
404                 TRACE_CATCH(result= zft_simple_write(&cnt,
405                                                      dst_buf, seg_sz,
406                                                      usr_buf, req_len,
407                                                      pos, volume),);
408         }
409         pos->volume_pos   += result;
410         pos->seg_byte_pos += cnt;
411         pos->tape_pos     += cnt;
412         TRACE(ft_t_data_flow, "\n"
413               KERN_INFO "removed from user-buffer : %d bytes.\n"
414               KERN_INFO "copied to zft_deblock_buf: %d bytes.\n"
415               KERN_INFO "zft_tape_pos             : " LL_X " bytes.",
416               result, cnt, LL(pos->tape_pos));
417         TRACE_EXIT result;
418 }
419
420
421 /*  called by the kernel-interface routine "zft_write()"
422  */
423 int _zft_write(const char __user *buff, int req_len)
424 {
425         int result = 0;
426         int written = 0;
427         int write_cnt;
428         int seg_sz;
429         static const zft_volinfo *volume = NULL;
430         TRACE_FUN(ft_t_flow);
431         
432         zft_resid         = req_len;    
433         last_write_failed = 1; /* reset to 0 when successful */
434         /* check if write is allowed 
435          */
436         TRACE_CATCH(check_write_access(req_len, &volume,&zft_pos,zft_blk_sz),);
437         while (req_len > 0) {
438                 /* Allow us to escape from this loop with a signal !
439                  */
440                 FT_SIGNAL_EXIT(_DONT_BLOCK);
441                 seg_sz = zft_get_seg_sz(zft_pos.seg_pos);
442                 if ((write_cnt = fill_deblock_buf(zft_deblock_buf,
443                                                   seg_sz,
444                                                   &zft_pos,
445                                                   volume,
446                                                   buff,
447                                                   req_len)) < 0) {
448                         zft_resid -= written;
449                         if (write_cnt == -ENOSPC) {
450                                 /* leave the remainder to flush_buffers()
451                                  */
452                                 TRACE(ft_t_info, "No space left on device");
453                                 last_write_failed = 0;
454                                 if (!need_flush) {
455                                         need_flush = written > 0;
456                                 }
457                                 TRACE_EXIT written > 0 ? written : -ENOSPC;
458                         } else {
459                                 TRACE_EXIT result;
460                         }
461                 }
462                 if (zft_pos.seg_byte_pos == seg_sz) {
463                         TRACE_CATCH(ftape_write_segment(zft_pos.seg_pos, 
464                                                         zft_deblock_buf,
465                                                         FT_WR_ASYNC),
466                                     zft_resid -= written);
467                         zft_written_segments ++;
468                         zft_pos.seg_byte_pos =  0;
469                         zft_deblock_segment  = zft_pos.seg_pos;
470                         ++zft_pos.seg_pos;
471                 }
472                 written += write_cnt;
473                 buff    += write_cnt;
474                 req_len -= write_cnt;
475         } /* while (req_len > 0) */
476         TRACE(ft_t_data_flow, "remaining in blocking buffer: %d",
477                zft_pos.seg_byte_pos);
478         TRACE(ft_t_data_flow, "just written bytes: %d", written);
479         last_write_failed = 0;
480         zft_resid -= written;
481         need_flush = need_flush || written > 0;
482         TRACE_EXIT written;               /* bytes written */
483 }