1 |
1275 |
phoenix |
/*
|
2 |
|
|
* Code extracted from drivers/block/genhd.c
|
3 |
|
|
* Copyright (C) 1991-1998 Linus Torvalds
|
4 |
|
|
* Re-organised Feb 1998 Russell King
|
5 |
|
|
*
|
6 |
|
|
* We now have independent partition support from the
|
7 |
|
|
* block drivers, which allows all the partition code to
|
8 |
|
|
* be grouped in one location, and it to be mostly self
|
9 |
|
|
* contained.
|
10 |
|
|
*
|
11 |
|
|
* Added needed MAJORS for new pairs, {hdi,hdj}, {hdk,hdl}
|
12 |
|
|
*/
|
13 |
|
|
|
14 |
|
|
#include <linux/config.h>
|
15 |
|
|
#include <linux/fs.h>
|
16 |
|
|
#include <linux/genhd.h>
|
17 |
|
|
#include <linux/kernel.h>
|
18 |
|
|
#include <linux/major.h>
|
19 |
|
|
#include <linux/blk.h>
|
20 |
|
|
#include <linux/init.h>
|
21 |
|
|
#include <linux/raid/md.h>
|
22 |
|
|
|
23 |
|
|
#include "check.h"
|
24 |
|
|
|
25 |
|
|
#include "acorn.h"
|
26 |
|
|
#include "amiga.h"
|
27 |
|
|
#include "atari.h"
|
28 |
|
|
#include "ldm.h"
|
29 |
|
|
#include "mac.h"
|
30 |
|
|
#include "msdos.h"
|
31 |
|
|
#include "osf.h"
|
32 |
|
|
#include "sgi.h"
|
33 |
|
|
#include "sun.h"
|
34 |
|
|
#include "ibm.h"
|
35 |
|
|
#include "ultrix.h"
|
36 |
|
|
#include "efi.h"
|
37 |
|
|
|
38 |
|
|
extern int *blk_size[];
|
39 |
|
|
|
40 |
|
|
int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/
|
41 |
|
|
|
42 |
|
|
static int (*check_part[])(struct gendisk *hd, struct block_device *bdev, unsigned long first_sect, int first_minor) = {
|
43 |
|
|
#ifdef CONFIG_ACORN_PARTITION
|
44 |
|
|
acorn_partition,
|
45 |
|
|
#endif
|
46 |
|
|
#ifdef CONFIG_SGI_PARTITION
|
47 |
|
|
sgi_partition,
|
48 |
|
|
#endif
|
49 |
|
|
#ifdef CONFIG_EFI_PARTITION
|
50 |
|
|
efi_partition, /* this must come before msdos */
|
51 |
|
|
#endif
|
52 |
|
|
#ifdef CONFIG_LDM_PARTITION
|
53 |
|
|
ldm_partition, /* this must come before msdos */
|
54 |
|
|
#endif
|
55 |
|
|
#ifdef CONFIG_MSDOS_PARTITION
|
56 |
|
|
msdos_partition,
|
57 |
|
|
#endif
|
58 |
|
|
#ifdef CONFIG_OSF_PARTITION
|
59 |
|
|
osf_partition,
|
60 |
|
|
#endif
|
61 |
|
|
#ifdef CONFIG_SUN_PARTITION
|
62 |
|
|
sun_partition,
|
63 |
|
|
#endif
|
64 |
|
|
#ifdef CONFIG_AMIGA_PARTITION
|
65 |
|
|
amiga_partition,
|
66 |
|
|
#endif
|
67 |
|
|
#ifdef CONFIG_ATARI_PARTITION
|
68 |
|
|
atari_partition,
|
69 |
|
|
#endif
|
70 |
|
|
#ifdef CONFIG_MAC_PARTITION
|
71 |
|
|
mac_partition,
|
72 |
|
|
#endif
|
73 |
|
|
#ifdef CONFIG_ULTRIX_PARTITION
|
74 |
|
|
ultrix_partition,
|
75 |
|
|
#endif
|
76 |
|
|
#ifdef CONFIG_IBM_PARTITION
|
77 |
|
|
ibm_partition,
|
78 |
|
|
#endif
|
79 |
|
|
NULL
|
80 |
|
|
};
|
81 |
|
|
|
82 |
|
|
/*
|
83 |
|
|
* This is ucking fugly but its probably the best thing for 2.4.x
|
84 |
|
|
* Take it as a clear reminder that: 1) we should put the device name
|
85 |
|
|
* generation in the object kdev_t points to in 2.5.
|
86 |
|
|
* and 2) ioctls better work on half-opened devices.
|
87 |
|
|
*/
|
88 |
|
|
|
89 |
|
|
#ifdef CONFIG_ARCH_S390
|
90 |
|
|
int (*genhd_dasd_name)(char*,int,int,struct gendisk*) = NULL;
|
91 |
|
|
int (*genhd_dasd_ioctl)(struct inode *inp, struct file *filp,
|
92 |
|
|
unsigned int no, unsigned long data);
|
93 |
|
|
EXPORT_SYMBOL(genhd_dasd_name);
|
94 |
|
|
EXPORT_SYMBOL(genhd_dasd_ioctl);
|
95 |
|
|
#endif
|
96 |
|
|
|
97 |
|
|
/*
|
98 |
|
|
* disk_name() is used by partition check code and the md driver.
|
99 |
|
|
* It formats the devicename of the indicated disk into
|
100 |
|
|
* the supplied buffer (of size at least 32), and returns
|
101 |
|
|
* a pointer to that same buffer (for convenience).
|
102 |
|
|
*/
|
103 |
|
|
|
104 |
|
|
char *disk_name (struct gendisk *hd, int minor, char *buf)
|
105 |
|
|
{
|
106 |
|
|
const char *maj = hd->major_name;
|
107 |
|
|
unsigned int unit = (minor >> hd->minor_shift);
|
108 |
|
|
unsigned int part = (minor & ((1 << hd->minor_shift) -1 ));
|
109 |
|
|
|
110 |
|
|
if ((unit < hd->nr_real) && hd->part[minor].de) {
|
111 |
|
|
int pos;
|
112 |
|
|
|
113 |
|
|
pos = devfs_generate_path (hd->part[minor].de, buf, 64);
|
114 |
|
|
if (pos >= 0)
|
115 |
|
|
return buf + pos;
|
116 |
|
|
}
|
117 |
|
|
|
118 |
|
|
#ifdef CONFIG_ARCH_S390
|
119 |
|
|
if (genhd_dasd_name
|
120 |
|
|
&& genhd_dasd_name (buf, unit, part, hd) == 0)
|
121 |
|
|
return buf;
|
122 |
|
|
#endif
|
123 |
|
|
/*
|
124 |
|
|
* IDE devices use multiple major numbers, but the drives
|
125 |
|
|
* are named as: {hda,hdb}, {hdc,hdd}, {hde,hdf}, {hdg,hdh}..
|
126 |
|
|
* This requires special handling here.
|
127 |
|
|
*/
|
128 |
|
|
switch (hd->major) {
|
129 |
|
|
case IDE9_MAJOR:
|
130 |
|
|
unit += 2;
|
131 |
|
|
case IDE8_MAJOR:
|
132 |
|
|
unit += 2;
|
133 |
|
|
case IDE7_MAJOR:
|
134 |
|
|
unit += 2;
|
135 |
|
|
case IDE6_MAJOR:
|
136 |
|
|
unit += 2;
|
137 |
|
|
case IDE5_MAJOR:
|
138 |
|
|
unit += 2;
|
139 |
|
|
case IDE4_MAJOR:
|
140 |
|
|
unit += 2;
|
141 |
|
|
case IDE3_MAJOR:
|
142 |
|
|
unit += 2;
|
143 |
|
|
case IDE2_MAJOR:
|
144 |
|
|
unit += 2;
|
145 |
|
|
case IDE1_MAJOR:
|
146 |
|
|
unit += 2;
|
147 |
|
|
case IDE0_MAJOR:
|
148 |
|
|
maj = "hd";
|
149 |
|
|
break;
|
150 |
|
|
case MD_MAJOR:
|
151 |
|
|
sprintf(buf, "%s%d", maj, unit);
|
152 |
|
|
return buf;
|
153 |
|
|
}
|
154 |
|
|
if (hd->major >= SCSI_DISK1_MAJOR && hd->major <= SCSI_DISK7_MAJOR) {
|
155 |
|
|
unit = unit + (hd->major - SCSI_DISK1_MAJOR + 1) * 16;
|
156 |
|
|
if (unit+'a' > 'z') {
|
157 |
|
|
unit -= 26;
|
158 |
|
|
sprintf(buf, "sd%c%c", 'a' + unit / 26, 'a' + unit % 26);
|
159 |
|
|
if (part)
|
160 |
|
|
sprintf(buf + 4, "%d", part);
|
161 |
|
|
return buf;
|
162 |
|
|
}
|
163 |
|
|
}
|
164 |
|
|
if (hd->major >= COMPAQ_SMART2_MAJOR && hd->major <= COMPAQ_SMART2_MAJOR+7) {
|
165 |
|
|
int ctlr = hd->major - COMPAQ_SMART2_MAJOR;
|
166 |
|
|
if (part == 0)
|
167 |
|
|
sprintf(buf, "%s/c%dd%d", maj, ctlr, unit);
|
168 |
|
|
else
|
169 |
|
|
sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, unit, part);
|
170 |
|
|
return buf;
|
171 |
|
|
}
|
172 |
|
|
if (hd->major >= COMPAQ_CISS_MAJOR && hd->major <= COMPAQ_CISS_MAJOR+7) {
|
173 |
|
|
int ctlr = hd->major - COMPAQ_CISS_MAJOR;
|
174 |
|
|
if (part == 0)
|
175 |
|
|
sprintf(buf, "%s/c%dd%d", maj, ctlr, unit);
|
176 |
|
|
else
|
177 |
|
|
sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, unit, part);
|
178 |
|
|
return buf;
|
179 |
|
|
}
|
180 |
|
|
if (hd->major >= DAC960_MAJOR && hd->major <= DAC960_MAJOR+7) {
|
181 |
|
|
int ctlr = hd->major - DAC960_MAJOR;
|
182 |
|
|
if (part == 0)
|
183 |
|
|
sprintf(buf, "%s/c%dd%d", maj, ctlr, unit);
|
184 |
|
|
else
|
185 |
|
|
sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, unit, part);
|
186 |
|
|
return buf;
|
187 |
|
|
}
|
188 |
|
|
if (hd->major == ATARAID_MAJOR) {
|
189 |
|
|
int disk = minor >> hd->minor_shift;
|
190 |
|
|
int part = minor & (( 1 << hd->minor_shift) - 1);
|
191 |
|
|
if (part == 0)
|
192 |
|
|
sprintf(buf, "%s/d%d", maj, disk);
|
193 |
|
|
else
|
194 |
|
|
sprintf(buf, "%s/d%dp%d", maj, disk, part);
|
195 |
|
|
return buf;
|
196 |
|
|
}
|
197 |
|
|
if (part)
|
198 |
|
|
sprintf(buf, "%s%c%d", maj, unit+'a', part);
|
199 |
|
|
else
|
200 |
|
|
sprintf(buf, "%s%c", maj, unit+'a');
|
201 |
|
|
return buf;
|
202 |
|
|
}
|
203 |
|
|
|
204 |
|
|
/*
|
205 |
|
|
* Add a partitions details to the devices partition description.
|
206 |
|
|
*/
|
207 |
|
|
void add_gd_partition(struct gendisk *hd, int minor, int start, int size)
|
208 |
|
|
{
|
209 |
|
|
#ifndef CONFIG_DEVFS_FS
|
210 |
|
|
char buf[40];
|
211 |
|
|
#endif
|
212 |
|
|
|
213 |
|
|
hd->part[minor].start_sect = start;
|
214 |
|
|
hd->part[minor].nr_sects = size;
|
215 |
|
|
#ifdef CONFIG_DEVFS_FS
|
216 |
|
|
printk(" p%d", (minor & ((1 << hd->minor_shift) - 1)));
|
217 |
|
|
#else
|
218 |
|
|
if ((hd->major >= COMPAQ_SMART2_MAJOR+0 && hd->major <= COMPAQ_SMART2_MAJOR+7) ||
|
219 |
|
|
(hd->major >= COMPAQ_CISS_MAJOR+0 && hd->major <= COMPAQ_CISS_MAJOR+7))
|
220 |
|
|
printk(" p%d", (minor & ((1 << hd->minor_shift) - 1)));
|
221 |
|
|
else
|
222 |
|
|
printk(" %s", disk_name(hd, minor, buf));
|
223 |
|
|
#endif
|
224 |
|
|
}
|
225 |
|
|
|
226 |
|
|
static void check_partition(struct gendisk *hd, kdev_t dev, int first_part_minor)
|
227 |
|
|
{
|
228 |
|
|
devfs_handle_t de = NULL;
|
229 |
|
|
static int first_time = 1;
|
230 |
|
|
unsigned long first_sector;
|
231 |
|
|
struct block_device *bdev;
|
232 |
|
|
char buf[64];
|
233 |
|
|
int i;
|
234 |
|
|
|
235 |
|
|
if (first_time)
|
236 |
|
|
printk(KERN_INFO "Partition check:\n");
|
237 |
|
|
first_time = 0;
|
238 |
|
|
first_sector = hd->part[MINOR(dev)].start_sect;
|
239 |
|
|
|
240 |
|
|
/*
|
241 |
|
|
* This is a kludge to allow the partition check to be
|
242 |
|
|
* skipped for specific drives (e.g. IDE CD-ROM drives)
|
243 |
|
|
*/
|
244 |
|
|
if ((int)first_sector == -1) {
|
245 |
|
|
hd->part[MINOR(dev)].start_sect = 0;
|
246 |
|
|
return;
|
247 |
|
|
}
|
248 |
|
|
|
249 |
|
|
if (hd->de_arr)
|
250 |
|
|
de = hd->de_arr[MINOR(dev) >> hd->minor_shift];
|
251 |
|
|
i = devfs_generate_path (de, buf, sizeof buf);
|
252 |
|
|
if (i >= 0)
|
253 |
|
|
printk(KERN_INFO " /dev/%s:", buf + i);
|
254 |
|
|
else
|
255 |
|
|
printk(KERN_INFO " %s:", disk_name(hd, MINOR(dev), buf));
|
256 |
|
|
bdev = bdget(kdev_t_to_nr(dev));
|
257 |
|
|
bdev->bd_inode->i_size = (loff_t)hd->part[MINOR(dev)].nr_sects << 9;
|
258 |
|
|
bdev->bd_inode->i_blkbits = blksize_bits(block_size(dev));
|
259 |
|
|
for (i = 0; check_part[i]; i++) {
|
260 |
|
|
int res;
|
261 |
|
|
res = check_part[i](hd, bdev, first_sector, first_part_minor);
|
262 |
|
|
if (res) {
|
263 |
|
|
if (res < 0 && warn_no_part)
|
264 |
|
|
printk(" unable to read partition table\n");
|
265 |
|
|
goto setup_devfs;
|
266 |
|
|
}
|
267 |
|
|
}
|
268 |
|
|
|
269 |
|
|
printk(" unknown partition table\n");
|
270 |
|
|
setup_devfs:
|
271 |
|
|
invalidate_bdev(bdev, 1);
|
272 |
|
|
truncate_inode_pages(bdev->bd_inode->i_mapping, 0);
|
273 |
|
|
bdput(bdev);
|
274 |
|
|
i = first_part_minor - 1;
|
275 |
|
|
devfs_register_partitions (hd, i, hd->sizes ? 0 : 1);
|
276 |
|
|
}
|
277 |
|
|
|
278 |
|
|
#ifdef CONFIG_DEVFS_FS
|
279 |
|
|
static void devfs_register_partition (struct gendisk *dev, int minor, int part)
|
280 |
|
|
{
|
281 |
|
|
int devnum = minor >> dev->minor_shift;
|
282 |
|
|
devfs_handle_t dir;
|
283 |
|
|
unsigned int devfs_flags = DEVFS_FL_DEFAULT;
|
284 |
|
|
char devname[16];
|
285 |
|
|
|
286 |
|
|
if (dev->part[minor + part].de) return;
|
287 |
|
|
dir = devfs_get_parent (dev->part[minor].de);
|
288 |
|
|
if (!dir) return;
|
289 |
|
|
if ( dev->flags && (dev->flags[devnum] & GENHD_FL_REMOVABLE) )
|
290 |
|
|
devfs_flags |= DEVFS_FL_REMOVABLE;
|
291 |
|
|
sprintf (devname, "part%d", part);
|
292 |
|
|
dev->part[minor + part].de =
|
293 |
|
|
devfs_register (dir, devname, devfs_flags,
|
294 |
|
|
dev->major, minor + part,
|
295 |
|
|
S_IFBLK | S_IRUSR | S_IWUSR,
|
296 |
|
|
dev->fops, NULL);
|
297 |
|
|
}
|
298 |
|
|
|
299 |
|
|
static struct unique_numspace disc_numspace = UNIQUE_NUMBERSPACE_INITIALISER;
|
300 |
|
|
|
301 |
|
|
static void devfs_register_disc (struct gendisk *dev, int minor)
|
302 |
|
|
{
|
303 |
|
|
int pos = 0;
|
304 |
|
|
int devnum = minor >> dev->minor_shift;
|
305 |
|
|
devfs_handle_t dir, slave;
|
306 |
|
|
unsigned int devfs_flags = DEVFS_FL_DEFAULT;
|
307 |
|
|
char dirname[64], symlink[16];
|
308 |
|
|
static devfs_handle_t devfs_handle;
|
309 |
|
|
|
310 |
|
|
if (dev->part[minor].de) return;
|
311 |
|
|
if ( dev->flags && (dev->flags[devnum] & GENHD_FL_REMOVABLE) )
|
312 |
|
|
devfs_flags |= DEVFS_FL_REMOVABLE;
|
313 |
|
|
if (dev->de_arr) {
|
314 |
|
|
dir = dev->de_arr[devnum];
|
315 |
|
|
if (!dir) /* Aware driver wants to block disc management */
|
316 |
|
|
return;
|
317 |
|
|
pos = devfs_generate_path (dir, dirname + 3, sizeof dirname-3);
|
318 |
|
|
if (pos < 0) return;
|
319 |
|
|
strncpy (dirname + pos, "../", 3);
|
320 |
|
|
}
|
321 |
|
|
else {
|
322 |
|
|
/* Unaware driver: construct "real" directory */
|
323 |
|
|
sprintf (dirname, "../%s/disc%d", dev->major_name, devnum);
|
324 |
|
|
dir = devfs_mk_dir (NULL, dirname + 3, NULL);
|
325 |
|
|
}
|
326 |
|
|
if (!devfs_handle)
|
327 |
|
|
devfs_handle = devfs_mk_dir (NULL, "discs", NULL);
|
328 |
|
|
dev->part[minor].number = devfs_alloc_unique_number (&disc_numspace);
|
329 |
|
|
sprintf (symlink, "disc%d", dev->part[minor].number);
|
330 |
|
|
devfs_mk_symlink (devfs_handle, symlink, DEVFS_FL_DEFAULT,
|
331 |
|
|
dirname + pos, &slave, NULL);
|
332 |
|
|
dev->part[minor].de =
|
333 |
|
|
devfs_register (dir, "disc", devfs_flags, dev->major, minor,
|
334 |
|
|
S_IFBLK | S_IRUSR | S_IWUSR, dev->fops, NULL);
|
335 |
|
|
devfs_auto_unregister (dev->part[minor].de, slave);
|
336 |
|
|
if (!dev->de_arr)
|
337 |
|
|
devfs_auto_unregister (slave, dir);
|
338 |
|
|
}
|
339 |
|
|
#endif /* CONFIG_DEVFS_FS */
|
340 |
|
|
|
341 |
|
|
void devfs_register_partitions (struct gendisk *dev, int minor, int unregister)
|
342 |
|
|
{
|
343 |
|
|
#ifdef CONFIG_DEVFS_FS
|
344 |
|
|
int part;
|
345 |
|
|
|
346 |
|
|
if (!unregister)
|
347 |
|
|
devfs_register_disc (dev, minor);
|
348 |
|
|
for (part = 1; part < dev->max_p; part++) {
|
349 |
|
|
if ( unregister || (dev->part[minor].nr_sects < 1) ||
|
350 |
|
|
(dev->part[part + minor].nr_sects < 1) ) {
|
351 |
|
|
devfs_unregister (dev->part[part + minor].de);
|
352 |
|
|
dev->part[part + minor].de = NULL;
|
353 |
|
|
continue;
|
354 |
|
|
}
|
355 |
|
|
devfs_register_partition (dev, minor, part);
|
356 |
|
|
}
|
357 |
|
|
if (unregister) {
|
358 |
|
|
devfs_unregister (dev->part[minor].de);
|
359 |
|
|
dev->part[minor].de = NULL;
|
360 |
|
|
devfs_dealloc_unique_number (&disc_numspace,
|
361 |
|
|
dev->part[minor].number);
|
362 |
|
|
}
|
363 |
|
|
#endif /* CONFIG_DEVFS_FS */
|
364 |
|
|
}
|
365 |
|
|
|
366 |
|
|
/*
|
367 |
|
|
* This function will re-read the partition tables for a given device,
|
368 |
|
|
* and set things back up again. There are some important caveats,
|
369 |
|
|
* however. You must ensure that no one is using the device, and no one
|
370 |
|
|
* can start using the device while this function is being executed.
|
371 |
|
|
*
|
372 |
|
|
* Much of the cleanup from the old partition tables should have already been
|
373 |
|
|
* done
|
374 |
|
|
*/
|
375 |
|
|
|
376 |
|
|
void register_disk(struct gendisk *gdev, kdev_t dev, unsigned minors,
|
377 |
|
|
struct block_device_operations *ops, long size)
|
378 |
|
|
{
|
379 |
|
|
if (!gdev)
|
380 |
|
|
return;
|
381 |
|
|
grok_partitions(gdev, MINOR(dev)>>gdev->minor_shift, minors, size);
|
382 |
|
|
}
|
383 |
|
|
|
384 |
|
|
void grok_partitions(struct gendisk *dev, int drive, unsigned minors, long size)
|
385 |
|
|
{
|
386 |
|
|
int i;
|
387 |
|
|
int first_minor = drive << dev->minor_shift;
|
388 |
|
|
int end_minor = first_minor + dev->max_p;
|
389 |
|
|
|
390 |
|
|
if(!dev->sizes)
|
391 |
|
|
blk_size[dev->major] = NULL;
|
392 |
|
|
|
393 |
|
|
dev->part[first_minor].nr_sects = size;
|
394 |
|
|
/* No such device or no minors to use for partitions */
|
395 |
|
|
if ( !size && dev->flags && (dev->flags[drive] & GENHD_FL_REMOVABLE) )
|
396 |
|
|
devfs_register_partitions (dev, first_minor, 0);
|
397 |
|
|
if (!size || minors == 1)
|
398 |
|
|
return;
|
399 |
|
|
|
400 |
|
|
if (dev->sizes) {
|
401 |
|
|
dev->sizes[first_minor] = size >> (BLOCK_SIZE_BITS - 9);
|
402 |
|
|
for (i = first_minor + 1; i < end_minor; i++)
|
403 |
|
|
dev->sizes[i] = 0;
|
404 |
|
|
}
|
405 |
|
|
blk_size[dev->major] = dev->sizes;
|
406 |
|
|
check_partition(dev, MKDEV(dev->major, first_minor), 1 + first_minor);
|
407 |
|
|
|
408 |
|
|
/*
|
409 |
|
|
* We need to set the sizes array before we will be able to access
|
410 |
|
|
* any of the partitions on this device.
|
411 |
|
|
*/
|
412 |
|
|
if (dev->sizes != NULL) { /* optional safeguard in ll_rw_blk.c */
|
413 |
|
|
for (i = first_minor; i < end_minor; i++)
|
414 |
|
|
dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
|
415 |
|
|
}
|
416 |
|
|
}
|
417 |
|
|
|
418 |
|
|
unsigned char *read_dev_sector(struct block_device *bdev, unsigned long n, Sector *p)
|
419 |
|
|
{
|
420 |
|
|
struct address_space *mapping = bdev->bd_inode->i_mapping;
|
421 |
|
|
int sect = PAGE_CACHE_SIZE / 512;
|
422 |
|
|
struct page *page;
|
423 |
|
|
|
424 |
|
|
page = read_cache_page(mapping, n/sect,
|
425 |
|
|
(filler_t *)mapping->a_ops->readpage, NULL);
|
426 |
|
|
if (!IS_ERR(page)) {
|
427 |
|
|
wait_on_page(page);
|
428 |
|
|
if (!Page_Uptodate(page))
|
429 |
|
|
goto fail;
|
430 |
|
|
if (PageError(page))
|
431 |
|
|
goto fail;
|
432 |
|
|
p->v = page;
|
433 |
|
|
return (unsigned char *)page_address(page) + 512 * (n % sect);
|
434 |
|
|
fail:
|
435 |
|
|
page_cache_release(page);
|
436 |
|
|
}
|
437 |
|
|
p->v = NULL;
|
438 |
|
|
return NULL;
|
439 |
|
|
}
|