]> git.neil.brown.name Git - lafs-utils.git/blob - lib/lafs_add_device.c
FORMAT CHANGE use 32bit block counts in segment usage file.
[lafs-utils.git] / lib / lafs_add_device.c
1 #define _GNU_SOURCE
2 #define _FILE_OFFSET_BITS 64
3
4 #include <unistd.h>
5 #include <lafs/lafs.h>
6 #include <talloc.h>
7 #include <memory.h>
8 #include <sys/mount.h>
9 #include <sys/stat.h>
10 #include <sys/time.h>
11 #include <fcntl.h>
12
13 /* Add a new device to lafs.
14  * This just sets up the internal data structures.
15  * We at least need to lafs_write_dev() to write metadata out, and may want some
16  * mount call to connect this to an active lafs.
17  */
18
19 static int destroy(struct lafs_device *dev)
20 {
21         close(dev->fd);
22         return 0;
23 }
24
25 static int get_logical_block_size(int fd)
26 {
27         struct stat stb;
28         int sfd;
29         int rv = -1;
30         char path[60];
31         char buf[20];
32         fstat(fd, &stb);
33         if ((stb.st_mode & S_IFMT) != S_IFBLK)
34                 return 512;
35         sprintf(path, "/sys/dev/block/%d:%d/queue/logical_block_size",
36                 major(stb.st_rdev), minor(stb.st_rdev));
37         sfd = open(path, O_RDONLY);
38         if (sfd >= 0) {
39                 int n = read(sfd, buf, sizeof(buf));
40                 if (n >= 0)
41                         buf[n] = 0;
42                 close(sfd);
43                 rv = atoi(buf);
44         }
45         if (rv > 0)
46                 return rv;
47         return 512;
48 }
49
50 struct lafs_device *lafs_add_device(struct lafs *fs, char *devname, int fd,
51                                     loff_t segblocks, loff_t strideblocks,
52                                     int width, int usage_inum)
53 {
54         struct lafs_device *dev = talloc(fs, struct lafs_device);
55         struct lafs_device *d2;
56         unsigned long long size;
57         int devblk;
58         loff_t seg;
59
60         memset(dev, 0, sizeof(*dev));
61         dev->fd = fd;
62         dev->seq = 1;
63         dev->devnum = fs->loaded_devs++;
64         dev->name = talloc_strdup(dev, devname);
65
66         dev->width = width;
67         dev->stride = strideblocks;
68         dev->segment_size = segblocks;
69         dev->usage_inum = usage_inum;
70         dev->next = fs->devs;
71         fs->devs = dev;
72         dev->fs = fs;
73         fs->devices++;
74
75         if (dev->segment_size > fs->max_segment)
76                 fs->max_segment = dev->segment_size;
77
78         if (dev->width * dev->stride <= dev->segment_size) {
79                 dev->tables_per_seg = dev->segment_size /
80                         dev->width / dev->stride;
81                 dev->rows_per_table = dev->stride;
82                 dev->segment_stride = dev->segment_size;
83         } else {
84                 dev->tables_per_seg = 1;
85                 dev->rows_per_table = dev->segment_size / dev->width;
86                 dev->segment_stride = dev->rows_per_table;
87         }
88         gettimeofday(&dev->ctime, NULL);
89
90         talloc_set_destructor(dev, destroy);
91
92         /* now need to work out where the metadata goes and how much room
93          * is left for segments.
94          * Device block needs 1K and goes once at start and once at end.
95          * Start location is 1K in unless basic block size is larger
96          */
97         if (ioctl(fd, BLKGETSIZE64, &size) < 0)
98                 size = lseek64(fd, 0, SEEK_END);
99         devblk = get_logical_block_size(fd);
100         if (devblk < LAFS_DEVBLK_SIZE)
101                 devblk = LAFS_DEVBLK_SIZE;
102
103         size &= ~(unsigned long long)(devblk-1);
104
105
106 #if 0
107         dev->devaddr[0] = devblk;
108 #else
109         dev->devaddr[0] = 0;
110 #endif
111         dev->devaddr[1] = size - devblk;
112         /* State block has size set by 'fs->statesize'.
113          * We have two at the start of the device and two at the end.
114          * If stride*width < segment size we put them at multiples of width*stride
115          * If stride*width > segment size we put them at multiples of statesize
116          */
117         /* FIXME just do the simple version for now */
118         if (dev->width != 1 || dev->stride != 1)
119                 abort();
120         if (devblk < fs->statesize)
121                 devblk = fs->statesize;
122         dev->stateaddr[0] = 2 * devblk;
123         dev->stateaddr[1] = 3 * devblk;
124         dev->stateaddr[2] = size - 2*devblk;
125         dev->stateaddr[3] = size - 3*devblk;
126
127         /* segments need to align with width*stride too - later */
128         dev->segment_offset = 4 * devblk;
129         dev->segment_stride = dev->segment_size;
130         dev->segment_count = (dev->stateaddr[3] - dev->segment_offset) / fs->blocksize / dev->segment_size;
131
132
133         dev->tablesize = ((dev->segment_count + (fs->blocksize >> USAGE_SHIFT) + 1)
134                           / (fs->blocksize >> USAGE_SHIFT));
135
136         dev->size = dev->segment_count * dev->segment_size;
137
138         /* Need to find a suitable offset */
139         dev->start = 0;
140         for (d2 = dev->next;  d2 ; d2 = d2->next) {
141                 if (dev->start < d2->start + d2->size &&
142                     dev->start + dev->size > d2->start) {
143                         dev->start = d2->start + d2->size;
144                         /* start again from top */
145                         d2 = dev;
146                 }
147         }
148
149         for (seg = 0; seg < dev->segment_count; seg++)
150                 if (lafs_add_free_seg(fs, dev->devnum, seg) == 0)
151                         break;
152
153         return dev;
154 }